From 760f7866032df14327aed591c39308104f0e4722 Mon Sep 17 00:00:00 2001 From: Laszlo Csomor Date: Mon, 19 Dec 2016 15:46:47 +0000 Subject: Bazel client: generalize path handling Use/implement utility methods to join paths, check if they are the root directory or are absolute, etc. Doing so (instead of say checking if a path starts with "/") allows for correct behavior on Windows. See https://github.com/bazelbuild/bazel/issues/2107 -- PiperOrigin-RevId: 142446027 MOS_MIGRATED_REVID=142446027 --- src/main/cpp/blaze.cc | 57 ++++++++++++++++++++-------------- src/main/cpp/blaze_util.cc | 10 +++--- src/main/cpp/blaze_util_platform.h | 3 ++ src/main/cpp/blaze_util_posix.cc | 12 +++++-- src/main/cpp/blaze_util_windows.cc | 18 +++++++++-- src/main/cpp/startup_options.cc | 13 ++++---- src/main/cpp/util/file_platform.h | 6 ++++ src/main/cpp/util/file_posix.cc | 8 ++++- src/main/cpp/util/file_windows.cc | 40 +++++++++++++++++------- src/main/cpp/workspace_layout.cc | 16 +++------- src/test/cpp/util/file_posix_test.cc | 29 +++++++++++++++++ src/test/cpp/util/file_windows_test.cc | 28 +++++++++++++++++ 12 files changed, 176 insertions(+), 64 deletions(-) diff --git a/src/main/cpp/blaze.cc b/src/main/cpp/blaze.cc index a80ba26f7d..89ab712a5f 100644 --- a/src/main/cpp/blaze.cc +++ b/src/main/cpp/blaze.cc @@ -316,7 +316,7 @@ static string GetInstallBase(const string &root, const string &self_path) { die(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR, "\nFailed to find install_base_key's in zip file"); } - return root + "/" + globals->install_md5; + return blaze_util::JoinPath(root, globals->install_md5); } // Escapes colons by replacing them with '_C' and underscores by replacing them @@ -606,13 +606,15 @@ static void StartServer(const WorkspaceLayout* workspace_layout, BlazeServerStartup** server_startup) { vector jvm_args_vector = GetArgumentArray(); string argument_string = GetArgumentString(jvm_args_vector); - string server_dir = globals->options->output_base + "/server"; + string server_dir = + blaze_util::JoinPath(globals->options->output_base, "server"); // Write the cmdline argument string to the server dir. If we get to this // point, there is no server running, so we don't overwrite the cmdline file // for the existing server. If might be that the server dies and the cmdline // file stays there, but that is not a problem, since we always check the // server, too. - blaze_util::WriteFile(argument_string, server_dir + "/cmdline"); + blaze_util::WriteFile(argument_string, + blaze_util::JoinPath(server_dir, "cmdline")); // unless we restarted for a new-version, mark this as initial start if (globals->restart_reason == NO_RESTART) { @@ -717,7 +719,8 @@ static int GetServerPid(const string &server_dir) { // Starts up a new server and connects to it. Exits if it didn't work not. static void StartServerAndConnect(const WorkspaceLayout* workspace_layout, BlazeServer *server) { - string server_dir = globals->options->output_base + "/server"; + string server_dir = + blaze_util::JoinPath(globals->options->output_base, "server"); // The server dir has the socket, so we don't allow access by other // users. @@ -880,14 +883,13 @@ static void ActuallyExtractData(const string &argv0, // Now walk up until embedded_binaries and sync every directory in between. // synced_directories is used to avoid syncing the same directory twice. - // The !directory.empty() and directory != "/" conditions are not strictly - // needed, but it makes this loop more robust, because otherwise, if due to - // some glitch, directory was not under embedded_binaries, it would get - // into an infinite loop. + // The !directory.empty() and !blaze_util::IsRootDirectory(directory) + // conditions are not strictly needed, but it makes this loop more robust, + // because otherwise, if due to some glitch, directory was not under + // embedded_binaries, it would get into an infinite loop. while (directory != embedded_binaries && - synced_directories.count(directory) == 0 && - !directory.empty() && - directory != "/") { + synced_directories.count(directory) == 0 && !directory.empty() && + !blaze_util::IsRootDirectory(directory)) { blaze_util::SyncFile(directory); synced_directories.insert(directory); directory = blaze_util::Dirname(directory); @@ -911,7 +913,8 @@ static void ExtractData(const string &self_path) { // Work in a temp dir to avoid races. string tmp_install = globals->options->install_base + ".tmp." + blaze::GetProcessIdAsString(); - string tmp_binaries = tmp_install + "/_embedded_binaries"; + string tmp_binaries = + blaze_util::JoinPath(tmp_install, "_embedded_binaries"); ActuallyExtractData(self_path, tmp_binaries); uint64_t et = GetMillisecondsMonotonic(); @@ -1017,7 +1020,8 @@ static void KillRunningServerIfDifferentStartupOptions(BlazeServer* server) { return; } - string cmdline_path = globals->options->output_base + "/server/cmdline"; + string cmdline_path = + blaze_util::JoinPath(globals->options->output_base, "server/cmdline"); string joined_arguments; // No, /proc/$PID/cmdline does not work, because it is limited to 4K. Even @@ -1050,7 +1054,8 @@ static void EnsureCorrectRunningVersion(BlazeServer* server) { // target dirs don't match, or if the symlink was not present, then kill any // running servers. Lastly, symlink to our installation so others know which // installation is running. - string installation_path = globals->options->output_base + "/install"; + string installation_path = + blaze_util::JoinPath(globals->options->output_base, "install"); string prev_installation; bool ok = ReadDirectorySymlink(installation_path, &prev_installation); if (!ok || !CompareAbsolutePaths( @@ -1170,7 +1175,8 @@ static void ComputeBaseDirectories(const WorkspaceLayout* workspace_layout, // but if an install_base is specified on the command line, we use that as // the base instead. if (globals->options->install_base.empty()) { - string install_user_root = globals->options->output_user_root + "/install"; + string install_user_root = + blaze_util::JoinPath(globals->options->output_user_root, "install"); globals->options->install_base = GetInstallBase(install_user_root, self_path); } else { @@ -1212,8 +1218,10 @@ static void ComputeBaseDirectories(const WorkspaceLayout* workspace_layout, "blaze_util::MakeCanonical('%s') failed", output_base); } - globals->lockfile = globals->options->output_base + "/lock"; - globals->jvm_log_file = globals->options->output_base + "/server/jvm.out"; + globals->lockfile = + blaze_util::JoinPath(globals->options->output_base, "lock"); + globals->jvm_log_file = + blaze_util::JoinPath(globals->options->output_base, "server/jvm.out"); } static void CheckEnvironment() { @@ -1260,10 +1268,10 @@ static void CheckEnvironment() { } static string CheckAndGetBinaryPath(const string& argv0) { - if (argv0[0] == '/') { + if (blaze_util::IsAbsolute(argv0)) { return argv0; } else { - string abs_path = globals->cwd + '/' + argv0; + string abs_path = blaze_util::JoinPath(globals->cwd, argv0); string resolved_path = blaze_util::MakeCanonical(abs_path.c_str()); if (!resolved_path.empty()) { return resolved_path; @@ -1374,13 +1382,15 @@ GrpcBlazeServer::~GrpcBlazeServer() { bool GrpcBlazeServer::Connect() { assert(!connected_); - std::string server_dir = globals->options->output_base + "/server"; + std::string server_dir = + blaze_util::JoinPath(globals->options->output_base, "server"); std::string port; std::string ipv4_prefix = "127.0.0.1:"; std::string ipv6_prefix_1 = "[0:0:0:0:0:0:0:1]:"; std::string ipv6_prefix_2 = "[::1]:"; - if (!blaze_util::ReadFile(server_dir + "/command_port", &port)) { + if (!blaze_util::ReadFile(blaze_util::JoinPath(server_dir, "command_port"), + &port)) { return false; } @@ -1391,11 +1401,12 @@ bool GrpcBlazeServer::Connect() { return false; } - if (!blaze_util::ReadFile(server_dir + "/request_cookie", &request_cookie_)) { + if (!blaze_util::ReadFile(blaze_util::JoinPath(server_dir, "request_cookie"), + &request_cookie_)) { return false; } - if (!blaze_util::ReadFile(server_dir + "/response_cookie", + if (!blaze_util::ReadFile(blaze_util::JoinPath(server_dir, "response_cookie"), &response_cookie_)) { return false; } diff --git a/src/main/cpp/blaze_util.cc b/src/main/cpp/blaze_util.cc index 99b2d201c2..30034e43c4 100644 --- a/src/main/cpp/blaze_util.cc +++ b/src/main/cpp/blaze_util.cc @@ -43,15 +43,13 @@ const char kServerPidSymlink[] = "server.pid"; string MakeAbsolute(const string &path) { // Check if path is already absolute. - if (path.empty() || path[0] == '/' || (isalpha(path[0]) && path[1] == ':')) { + // TODO(laszlocsomor): remove the "path.empty() ||" clause; empty paths are + // not absolute! + if (path.empty() || blaze_util::IsAbsolute(path)) { return path; } - string cwd = blaze_util::GetCwd(); - - // Determine whether the cwd ends with "/" or not. - string separator = cwd.back() == '/' ? "" : "/"; - return cwd + separator + path; + return blaze_util::JoinPath(blaze_util::GetCwd(), path); } const char* GetUnaryOption(const char *arg, diff --git a/src/main/cpp/blaze_util_platform.h b/src/main/cpp/blaze_util_platform.h index 873d00597a..9fd0338857 100644 --- a/src/main/cpp/blaze_util_platform.h +++ b/src/main/cpp/blaze_util_platform.h @@ -56,6 +56,9 @@ std::string GetSelfPath(); // Returns the directory Bazel can use to store output. std::string GetOutputRoot(); +// Returns the location of the global bazelrc file if it exists, otherwise "". +std::string FindSystemWideBlazerc(); + // Warn about dubious filesystem types, such as NFS, case-insensitive (?). void WarnFilesystemType(const std::string& output_base); diff --git a/src/main/cpp/blaze_util_posix.cc b/src/main/cpp/blaze_util_posix.cc index a90c602639..9be1c4e7ce 100644 --- a/src/main/cpp/blaze_util_posix.cc +++ b/src/main/cpp/blaze_util_posix.cc @@ -125,6 +125,14 @@ string GetProcessIdAsString() { return ToString(getpid()); } +string FindSystemWideBlazerc() { + string path = "/etc/bazel.bazelrc"; + if (blaze_util::CanAccess(path, true, false, false)) { + return path; + } + return ""; +} + void ExecuteProgram(const string &exe, const vector &args_vector) { if (VerboseLogging()) { string dbg; @@ -323,7 +331,7 @@ string GetHashedBaseDir(const string& root, const string& hashable) { blaze_util::Md5Digest digest; digest.Update(hashable.data(), hashable.size()); digest.Finish(buf); - return root + "/" + digest.String(); + return blaze_util::JoinPath(root, digest.String()); } void CreateSecureOutputRoot(const string& path) { @@ -424,7 +432,7 @@ void SigPrintf(const char *format, ...) { uint64_t AcquireLock(const string& output_base, bool batch_mode, bool block, BlazeLock* blaze_lock) { - string lockfile = output_base + "/lock"; + string lockfile = blaze_util::JoinPath(output_base, "lock"); int lockfd = open(lockfile.c_str(), O_CREAT|O_RDWR, 0644); if (lockfd < 0) { diff --git a/src/main/cpp/blaze_util_windows.cc b/src/main/cpp/blaze_util_windows.cc index d74bd8d9a2..fbabf07297 100644 --- a/src/main/cpp/blaze_util_windows.cc +++ b/src/main/cpp/blaze_util_windows.cc @@ -300,6 +300,20 @@ string GetOutputRoot() { #endif // COMPILER_MSVC } +string FindSystemWideBlazerc() { +#ifdef COMPILER_MSVC + // TODO(bazel-team): implement this. + pdie(255, "FindSystemWideBlazer is not yet implemented on Windows"); + return ""; +#else // not COMPILER_MSVC + string path = "/etc/bazel.bazelrc"; + if (blaze_util::CanAccess(path, true, false, false)) { + return path; + } + return ""; +#endif // COMPILER_MSVC +} + uint64_t GetMillisecondsMonotonic() { return WindowsClock::INSTANCE.GetMilliseconds(); } @@ -1050,7 +1064,7 @@ string GetHashedBaseDir(const string& root, const string& hashable) { coded_name[i] = alphabet[buf[i] & 0x3F]; } coded_name[filename_length] = '\0'; - return root + "/" + string(coded_name); + return blaze_util::JoinPath(root, string(coded_name)); } void CreateSecureOutputRoot(const string& path) { @@ -1217,7 +1231,7 @@ uint64_t AcquireLock(const string& output_base, bool batch_mode, bool block, pdie(255, "blaze::AcquireLock is not implemented on Windows"); return 0; #else // not COMPILER_MSVC - string lockfile = output_base + "/lock"; + string lockfile = blaze_util::JoinPath(output_base, "lock"); int lockfd = open(lockfile.c_str(), O_CREAT|O_RDWR, 0644); if (lockfd < 0) { diff --git a/src/main/cpp/startup_options.cc b/src/main/cpp/startup_options.cc index ea92cff9fa..c69cf0bac3 100644 --- a/src/main/cpp/startup_options.cc +++ b/src/main/cpp/startup_options.cc @@ -354,7 +354,7 @@ string StartupOptions::GetHostJavabase() { } string StartupOptions::GetJvm() { - string java_program = GetHostJavabase() + "/bin/java"; + string java_program = blaze_util::JoinPath(GetHostJavabase(), "bin/java"); if (!blaze_util::CanAccess(java_program, false, false, true)) { if (!blaze_util::PathExists(java_program)) { fprintf(stderr, "Couldn't find java at '%s'.\n", java_program.c_str()); @@ -365,9 +365,9 @@ string StartupOptions::GetJvm() { exit(1); } // If the full JDK is installed - string jdk_rt_jar = GetHostJavabase() + "/jre/lib/rt.jar"; + string jdk_rt_jar = blaze_util::JoinPath(GetHostJavabase(), "jre/lib/rt.jar"); // If just the JRE is installed - string jre_rt_jar = GetHostJavabase() + "/lib/rt.jar"; + string jre_rt_jar = blaze_util::JoinPath(GetHostJavabase(), "lib/rt.jar"); if (blaze_util::CanAccess(jdk_rt_jar, true, false, false) || blaze_util::CanAccess(jre_rt_jar, true, false, false)) { return java_program; @@ -397,13 +397,14 @@ blaze_exit_code::ExitCode StartupOptions::AddJVMArguments( const string &host_javabase, vector *result, const vector &user_options, string *error) const { // Configure logging - const string propFile = output_base + "/javalog.properties"; + const string propFile = + blaze_util::JoinPath(output_base, "javalog.properties"); if (!blaze_util::WriteFile("handlers=java.util.logging.FileHandler\n" ".level=INFO\n" "java.util.logging.FileHandler.level=INFO\n" "java.util.logging.FileHandler.pattern=" + - output_base + - "/java.log\n" + blaze_util::JoinPath(output_base, "java.log") + + "\n" "java.util.logging.FileHandler.limit=50000\n" "java.util.logging.FileHandler.count=1\n" "java.util.logging.FileHandler.formatter=" diff --git a/src/main/cpp/util/file_platform.h b/src/main/cpp/util/file_platform.h index 78322dfeb8..e6df884ea0 100644 --- a/src/main/cpp/util/file_platform.h +++ b/src/main/cpp/util/file_platform.h @@ -69,6 +69,12 @@ bool CanAccess(const std::string& path, bool read, bool write, bool exec); // Returns true if `path` refers to a directory or a symlink/junction to one. bool IsDirectory(const std::string& path); +// Returns true if `path` is the root directory or a Windows drive root. +bool IsRootDirectory(const std::string &path); + +// Returns true if `path` is absolute. +bool IsAbsolute(const std::string &path); + // Calls fsync() on the file (or directory) specified in 'file_path'. // pdie() if syncing fails. void SyncFile(const std::string& path); diff --git a/src/main/cpp/util/file_posix.cc b/src/main/cpp/util/file_posix.cc index 9e835d759e..cb7bd8bd89 100644 --- a/src/main/cpp/util/file_posix.cc +++ b/src/main/cpp/util/file_posix.cc @@ -77,7 +77,7 @@ static bool GetDirectoryStat(const string &path, mode_t mode, } static bool MakeDirectories(const string &path, mode_t mode, bool childmost) { - if (path.empty() || path == "/") { + if (path.empty() || IsRootDirectory(path)) { errno = EACCES; return false; } @@ -272,6 +272,12 @@ bool IsDirectory(const string& path) { return stat(path.c_str(), &buf) == 0 && S_ISDIR(buf.st_mode); } +bool IsRootDirectory(const string &path) { + return path.size() == 1 && path[0] == '/'; +} + +bool IsAbsolute(const string &path) { return !path.empty() && path[0] == '/'; } + void SyncFile(const string& path) { // fsync always fails on Cygwin with "Permission denied" for some reason. #ifndef __CYGWIN__ diff --git a/src/main/cpp/util/file_windows.cc b/src/main/cpp/util/file_windows.cc index 1d00269002..bf6e79cf1e 100644 --- a/src/main/cpp/util/file_windows.cc +++ b/src/main/cpp/util/file_windows.cc @@ -13,6 +13,7 @@ // limitations under the License. #include "src/main/cpp/util/file_platform.h" +#include // isalpha #include #include "src/main/cpp/util/errors.h" @@ -72,22 +73,30 @@ IPipe* CreatePipe() { return new WindowsPipe(read_handle, write_handle); } -static bool IsRootDirectory(const string& path) { - // Return true if path is "/", "\", "c:/", "c:\", "\\?\c:\", or "\??\c:\". +// Checks if the path is absolute and/or is a root path. +// +// If `must_be_root` is true, then in addition to being absolute, the path must +// also be just the root part, no other components, e.g. "c:\" is both absolute +// and root, but "c:\foo" is just absolute. +static bool IsRootOrAbsolute(const string& path, bool must_be_root) { + // An absolute path is one that starts with "/", "\", "c:/", "c:\", + // "\\?\c:\", or "\??\c:\". // // It is unclear whether the UNC prefix is just "\\?\" or is "\??\" also // valid (in some cases it seems to be, though MSDN doesn't mention it). return - // path is "/" or "\" - (path.size() == 1 && (path[0] == '/' || path[0] == '\\')) || - // path is "c:/" or "c:\" - (path.size() == 3 && isalpha(path[0]) && path[1] == ':' && + // path is (or starts with) "/" or "\" + ((must_be_root ? path.size() == 1 : !path.empty()) && + (path[0] == '/' || path[0] == '\\')) || + // path is (or starts with) "c:/" or "c:\" or similar + ((must_be_root ? path.size() == 3 : path.size() >= 3) && + isalpha(path[0]) && path[1] == ':' && (path[2] == '/' || path[2] == '\\')) || - // path is "\\?\c:\" or "\??\c:\" - (path.size() == 7 && path[0] == '\\' && - (path[1] == '\\' || path[1] == '?') && path[2] == '?' && - path[3] == '\\' && isalpha(path[4]) && path[5] == ':' && - path[6] == '\\'); + // path is (or starts with) "\\?\c:\" or "\??\c:\" or similar + ((must_be_root ? path.size() == 7 : path.size() >= 7) && + path[0] == '\\' && (path[1] == '\\' || path[1] == '?') && + path[2] == '?' && path[3] == '\\' && isalpha(path[4]) && + path[5] == ':' && path[6] == '\\'); } pair SplitPath(const string& path) { @@ -107,7 +116,8 @@ pair SplitPath(const string& path) { // Include the "/" or "\" in the drive specifier. path.substr(0, pos + 1), path.substr(pos + 1)); } else { - // Unix path, or relative path. + // Windows path (neither top-level nor drive root), Unix path, or + // relative path. return std::make_pair( // If the only "/" is the leading one, then that shall be the first // pair element, otherwise the substring up to the rightmost "/". @@ -193,6 +203,12 @@ bool IsDirectory(const string& path) { #else // not COMPILER_MSVC #endif // COMPILER_MSVC +bool IsRootDirectory(const string& path) { + return IsRootOrAbsolute(path, true); +} + +bool IsAbsolute(const string& path) { return IsRootOrAbsolute(path, false); } + #ifdef COMPILER_MSVC void SyncFile(const string& path) { // No-op on Windows native; unsupported by Cygwin. diff --git a/src/main/cpp/workspace_layout.cc b/src/main/cpp/workspace_layout.cc index 1e22a1d16a..330d2e9b28 100644 --- a/src/main/cpp/workspace_layout.cc +++ b/src/main/cpp/workspace_layout.cc @@ -45,7 +45,7 @@ string WorkspaceLayout::GetWorkspace(const string &cwd) const { return workspace; } workspace = blaze_util::Dirname(workspace); - } while (!workspace.empty() && workspace != "/"); + } while (!workspace.empty() && !blaze_util::IsRootDirectory(workspace)); return ""; } @@ -72,9 +72,9 @@ static string FindAlongsideBinaryBlazerc(const string& cwd, const string& path_to_binary) { // TODO(b/32115171): This doesn't work on Windows. Fix this together with the // associated bug. - const string path = path_to_binary[0] == '/' - ? path_to_binary - : blaze_util::JoinPath(cwd, path_to_binary); + const string path = blaze_util::IsAbsolute(path_to_binary) + ? path_to_binary + : blaze_util::JoinPath(cwd, path_to_binary); const string base = blaze_util::Basename(path_to_binary); const string binary_blazerc_path = path + "." + base + "rc"; if (blaze_util::CanAccess(binary_blazerc_path, true, false, false)) { @@ -83,14 +83,6 @@ static string FindAlongsideBinaryBlazerc(const string& cwd, return ""; } -static string FindSystemWideBlazerc() { - string path = "/etc/bazel.bazelrc"; - if (blaze_util::CanAccess(path, true, false, false)) { - return path; - } - return ""; -} - void WorkspaceLayout::FindCandidateBlazercPaths( const string& workspace, const string& cwd, diff --git a/src/test/cpp/util/file_posix_test.cc b/src/test/cpp/util/file_posix_test.cc index 0acd392bf6..110a059a92 100644 --- a/src/test/cpp/util/file_posix_test.cc +++ b/src/test/cpp/util/file_posix_test.cc @@ -211,6 +211,7 @@ TEST(FileTest, HammerMakeDirectories) { // ASSERT_LE(0, fork()); // ASSERT_TRUE(MakeDirectories(path, 0755)); } + TEST(FilePosixTest, Which) { ASSERT_EQ("", Which("")); ASSERT_EQ("", Which("foo")); @@ -407,4 +408,32 @@ TEST(FilePosixTest, ForEachDirectoryEntry) { rmdir(root.c_str()); } +TEST(FileTest, IsAbsolute) { + ASSERT_FALSE(IsAbsolute("")); + ASSERT_TRUE(IsAbsolute("/")); + ASSERT_TRUE(IsAbsolute("/foo")); + ASSERT_FALSE(IsAbsolute("\\")); + ASSERT_FALSE(IsAbsolute("\\foo")); + ASSERT_FALSE(IsAbsolute("c:")); + ASSERT_FALSE(IsAbsolute("c:/")); + ASSERT_FALSE(IsAbsolute("c:\\")); + ASSERT_FALSE(IsAbsolute("c:\\foo")); + ASSERT_FALSE(IsAbsolute("\\\\?\\c:\\")); + ASSERT_FALSE(IsAbsolute("\\\\?\\c:\\foo")); +} + +TEST(FileTest, IsRootDirectory) { + ASSERT_FALSE(IsRootDirectory("")); + ASSERT_TRUE(IsRootDirectory("/")); + ASSERT_FALSE(IsRootDirectory("/foo")); + ASSERT_FALSE(IsRootDirectory("\\")); + ASSERT_FALSE(IsRootDirectory("\\foo")); + ASSERT_FALSE(IsRootDirectory("c:")); + ASSERT_FALSE(IsRootDirectory("c:/")); + ASSERT_FALSE(IsRootDirectory("c:\\")); + ASSERT_FALSE(IsRootDirectory("c:\\foo")); + ASSERT_FALSE(IsRootDirectory("\\\\?\\c:\\")); + ASSERT_FALSE(IsRootDirectory("\\\\?\\c:\\foo")); +} + } // namespace blaze_util diff --git a/src/test/cpp/util/file_windows_test.cc b/src/test/cpp/util/file_windows_test.cc index 85cc7a491e..00486d4fa9 100644 --- a/src/test/cpp/util/file_windows_test.cc +++ b/src/test/cpp/util/file_windows_test.cc @@ -65,4 +65,32 @@ TEST(FileTest, TestBasename) { ASSERT_EQ("foo", Basename("\\\\?\\c:\\foo")); } +TEST(FileTest, IsAbsolute) { + ASSERT_FALSE(IsAbsolute("")); + ASSERT_TRUE(IsAbsolute("/")); + ASSERT_TRUE(IsAbsolute("/foo")); + ASSERT_TRUE(IsAbsolute("\\")); + ASSERT_TRUE(IsAbsolute("\\foo")); + ASSERT_FALSE(IsAbsolute("c:")); + ASSERT_TRUE(IsAbsolute("c:/")); + ASSERT_TRUE(IsAbsolute("c:\\")); + ASSERT_TRUE(IsAbsolute("c:\\foo")); + ASSERT_TRUE(IsAbsolute("\\\\?\\c:\\")); + ASSERT_TRUE(IsAbsolute("\\\\?\\c:\\foo")); +} + +TEST(FileTest, IsRootDirectory) { + ASSERT_FALSE(IsRootDirectory("")); + ASSERT_TRUE(IsRootDirectory("/")); + ASSERT_FALSE(IsRootDirectory("/foo")); + ASSERT_TRUE(IsRootDirectory("\\")); + ASSERT_FALSE(IsRootDirectory("\\foo")); + ASSERT_FALSE(IsRootDirectory("c:")); + ASSERT_TRUE(IsRootDirectory("c:/")); + ASSERT_TRUE(IsRootDirectory("c:\\")); + ASSERT_FALSE(IsRootDirectory("c:\\foo")); + ASSERT_TRUE(IsRootDirectory("\\\\?\\c:\\")); + ASSERT_FALSE(IsRootDirectory("\\\\?\\c:\\foo")); +} + } // namespace blaze_util -- cgit v1.2.3