1fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// Copyright 2008, Google Inc.
2fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// All rights reserved.
3fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt//
4fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// Redistribution and use in source and binary forms, with or without
5fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// modification, are permitted provided that the following conditions are
6fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// met:
7fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt//
8fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt//     * Redistributions of source code must retain the above copyright
9fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// notice, this list of conditions and the following disclaimer.
10fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt//     * Redistributions in binary form must reproduce the above
11fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// copyright notice, this list of conditions and the following disclaimer
12fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// in the documentation and/or other materials provided with the
13fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// distribution.
14fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt//     * Neither the name of Google Inc. nor the names of its
15fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// contributors may be used to endorse or promote products derived from
16fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// this software without specific prior written permission.
17fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt//
18fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt//
30fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// Authors: keith.ray@gmail.com (Keith Ray)
31fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt
32fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt#include "gtest/internal/gtest-filepath.h"
33fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt#include "gtest/internal/gtest-port.h"
34fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt
35fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt#include <stdlib.h>
36fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt
37fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt#if GTEST_OS_WINDOWS_MOBILE
38fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt# include <windows.h>
39fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt#elif GTEST_OS_WINDOWS
40fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt# include <direct.h>
41fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt# include <io.h>
42fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt#elif GTEST_OS_SYMBIAN || GTEST_OS_NACL
43fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// Symbian OpenC and NaCl have PATH_MAX in sys/syslimits.h
44fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt# include <sys/syslimits.h>
45fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt#else
46fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt# include <limits.h>
47fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt# include <climits>  // Some Linux distributions define PATH_MAX here.
48fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt#endif  // GTEST_OS_WINDOWS_MOBILE
49fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt
50fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt#if GTEST_OS_WINDOWS
51fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt# define GTEST_PATH_MAX_ _MAX_PATH
52fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt#elif defined(PATH_MAX)
53fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt# define GTEST_PATH_MAX_ PATH_MAX
54fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt#elif defined(_XOPEN_PATH_MAX)
55fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt# define GTEST_PATH_MAX_ _XOPEN_PATH_MAX
56fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt#else
57fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt# define GTEST_PATH_MAX_ _POSIX_PATH_MAX
58fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt#endif  // GTEST_OS_WINDOWS
59fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt
60fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt#include "gtest/internal/gtest-string.h"
61fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt
62fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholtnamespace testing {
63fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholtnamespace internal {
64fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt
65fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt#if GTEST_OS_WINDOWS
66fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// On Windows, '\\' is the standard path separator, but many tools and the
67fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// Windows API also accept '/' as an alternate path separator. Unless otherwise
68fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// noted, a file path can contain either kind of path separators, or a mixture
69fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// of them.
70fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholtconst char kPathSeparator = '\\';
71fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholtconst char kAlternatePathSeparator = '/';
72fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholtconst char kPathSeparatorString[] = "\\";
73fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholtconst char kAlternatePathSeparatorString[] = "/";
74fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt# if GTEST_OS_WINDOWS_MOBILE
75fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// Windows CE doesn't have a current directory. You should not use
76fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// the current directory in tests on Windows CE, but this at least
77fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// provides a reasonable fallback.
78fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholtconst char kCurrentDirectoryString[] = "\\";
79fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// Windows CE doesn't define INVALID_FILE_ATTRIBUTES
80fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholtconst DWORD kInvalidFileAttributes = 0xffffffff;
81fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt# else
82fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholtconst char kCurrentDirectoryString[] = ".\\";
83fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt# endif  // GTEST_OS_WINDOWS_MOBILE
84fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt#else
85fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholtconst char kPathSeparator = '/';
86fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholtconst char kPathSeparatorString[] = "/";
87fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholtconst char kCurrentDirectoryString[] = "./";
88fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt#endif  // GTEST_OS_WINDOWS
89fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt
90fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// Returns whether the given character is a valid path separator.
91fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholtstatic bool IsPathSeparator(char c) {
92fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt#if GTEST_HAS_ALT_PATH_SEP_
93fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  return (c == kPathSeparator) || (c == kAlternatePathSeparator);
94fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt#else
95fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  return c == kPathSeparator;
96fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt#endif
97fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt}
98fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt
99fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// Returns the current working directory, or "" if unsuccessful.
100fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric AnholtFilePath FilePath::GetCurrentDir() {
101fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt#if GTEST_OS_WINDOWS_MOBILE
102fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  // Windows CE doesn't have a current directory, so we just return
103fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  // something reasonable.
104fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  return FilePath(kCurrentDirectoryString);
105fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt#elif GTEST_OS_WINDOWS
106fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  char cwd[GTEST_PATH_MAX_ + 1] = { '\0' };
107fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  return FilePath(_getcwd(cwd, sizeof(cwd)) == NULL ? "" : cwd);
108fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt#else
109fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  char cwd[GTEST_PATH_MAX_ + 1] = { '\0' };
110fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  return FilePath(getcwd(cwd, sizeof(cwd)) == NULL ? "" : cwd);
111fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt#endif  // GTEST_OS_WINDOWS_MOBILE
112fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt}
113fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt
114fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// Returns a copy of the FilePath with the case-insensitive extension removed.
115fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// Example: FilePath("dir/file.exe").RemoveExtension("EXE") returns
116fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// FilePath("dir/file"). If a case-insensitive extension is not
117fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// found, returns a copy of the original FilePath.
118fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric AnholtFilePath FilePath::RemoveExtension(const char* extension) const {
119fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  String dot_extension(String::Format(".%s", extension));
120fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  if (pathname_.EndsWithCaseInsensitive(dot_extension.c_str())) {
121fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt    return FilePath(String(pathname_.c_str(), pathname_.length() - 4));
122fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  }
123fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  return *this;
124fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt}
125fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt
126fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// Returns a pointer to the last occurence of a valid path separator in
127fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// the FilePath. On Windows, for example, both '/' and '\' are valid path
128fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// separators. Returns NULL if no path separator was found.
129fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholtconst char* FilePath::FindLastPathSeparator() const {
130fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  const char* const last_sep = strrchr(c_str(), kPathSeparator);
131fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt#if GTEST_HAS_ALT_PATH_SEP_
132fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  const char* const last_alt_sep = strrchr(c_str(), kAlternatePathSeparator);
133fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  // Comparing two pointers of which only one is NULL is undefined.
134fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  if (last_alt_sep != NULL &&
135fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt      (last_sep == NULL || last_alt_sep > last_sep)) {
136fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt    return last_alt_sep;
137fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  }
138fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt#endif
139fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  return last_sep;
140fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt}
141fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt
142fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// Returns a copy of the FilePath with the directory part removed.
143fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// Example: FilePath("path/to/file").RemoveDirectoryName() returns
144fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// FilePath("file"). If there is no directory part ("just_a_file"), it returns
145fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// the FilePath unmodified. If there is no file part ("just_a_dir/") it
146fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// returns an empty FilePath ("").
147fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// On Windows platform, '\' is the path separator, otherwise it is '/'.
148fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric AnholtFilePath FilePath::RemoveDirectoryName() const {
149fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  const char* const last_sep = FindLastPathSeparator();
150fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  return last_sep ? FilePath(String(last_sep + 1)) : *this;
151fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt}
152fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt
153fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// RemoveFileName returns the directory path with the filename removed.
154fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// Example: FilePath("path/to/file").RemoveFileName() returns "path/to/".
155fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// If the FilePath is "a_file" or "/a_file", RemoveFileName returns
156fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// FilePath("./") or, on Windows, FilePath(".\\"). If the filepath does
157fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// not have a file, like "just/a/dir/", it returns the FilePath unmodified.
158fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// On Windows platform, '\' is the path separator, otherwise it is '/'.
159fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric AnholtFilePath FilePath::RemoveFileName() const {
160fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  const char* const last_sep = FindLastPathSeparator();
161fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  String dir;
162fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  if (last_sep) {
163fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt    dir = String(c_str(), last_sep + 1 - c_str());
164fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  } else {
165fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt    dir = kCurrentDirectoryString;
166fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  }
167fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  return FilePath(dir);
168fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt}
169fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt
170fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// Helper functions for naming files in a directory for xml output.
171fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt
172fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// Given directory = "dir", base_name = "test", number = 0,
173fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// extension = "xml", returns "dir/test.xml". If number is greater
174fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// than zero (e.g., 12), returns "dir/test_12.xml".
175fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// On Windows platform, uses \ as the separator rather than /.
176fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric AnholtFilePath FilePath::MakeFileName(const FilePath& directory,
177fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt                                const FilePath& base_name,
178fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt                                int number,
179fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt                                const char* extension) {
180fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  String file;
181fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  if (number == 0) {
182fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt    file = String::Format("%s.%s", base_name.c_str(), extension);
183fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  } else {
184fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt    file = String::Format("%s_%d.%s", base_name.c_str(), number, extension);
185fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  }
186fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  return ConcatPaths(directory, FilePath(file));
187fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt}
188fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt
189fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// Given directory = "dir", relative_path = "test.xml", returns "dir/test.xml".
190fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// On Windows, uses \ as the separator rather than /.
191fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric AnholtFilePath FilePath::ConcatPaths(const FilePath& directory,
192fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt                               const FilePath& relative_path) {
193fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  if (directory.IsEmpty())
194fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt    return relative_path;
195fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  const FilePath dir(directory.RemoveTrailingPathSeparator());
196fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  return FilePath(String::Format("%s%c%s", dir.c_str(), kPathSeparator,
197fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt                                 relative_path.c_str()));
198fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt}
199fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt
200fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// Returns true if pathname describes something findable in the file-system,
201fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// either a file, directory, or whatever.
202fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholtbool FilePath::FileOrDirectoryExists() const {
203fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt#if GTEST_OS_WINDOWS_MOBILE
204fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  LPCWSTR unicode = String::AnsiToUtf16(pathname_.c_str());
205fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  const DWORD attributes = GetFileAttributes(unicode);
206fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  delete [] unicode;
207fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  return attributes != kInvalidFileAttributes;
208fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt#else
209fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  posix::StatStruct file_stat;
210fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  return posix::Stat(pathname_.c_str(), &file_stat) == 0;
211fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt#endif  // GTEST_OS_WINDOWS_MOBILE
212fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt}
213fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt
214fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// Returns true if pathname describes a directory in the file-system
215fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// that exists.
216fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholtbool FilePath::DirectoryExists() const {
217fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  bool result = false;
218fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt#if GTEST_OS_WINDOWS
219fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  // Don't strip off trailing separator if path is a root directory on
220fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  // Windows (like "C:\\").
221fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  const FilePath& path(IsRootDirectory() ? *this :
222fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt                                           RemoveTrailingPathSeparator());
223fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt#else
224fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  const FilePath& path(*this);
225fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt#endif
226fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt
227fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt#if GTEST_OS_WINDOWS_MOBILE
228fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  LPCWSTR unicode = String::AnsiToUtf16(path.c_str());
229fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  const DWORD attributes = GetFileAttributes(unicode);
230fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  delete [] unicode;
231fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  if ((attributes != kInvalidFileAttributes) &&
232fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt      (attributes & FILE_ATTRIBUTE_DIRECTORY)) {
233fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt    result = true;
234fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  }
235fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt#else
236fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  posix::StatStruct file_stat;
237fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  result = posix::Stat(path.c_str(), &file_stat) == 0 &&
238fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt      posix::IsDir(file_stat);
239fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt#endif  // GTEST_OS_WINDOWS_MOBILE
240fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt
241fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  return result;
242fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt}
243fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt
244fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// Returns true if pathname describes a root directory. (Windows has one
245fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// root directory per disk drive.)
246fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholtbool FilePath::IsRootDirectory() const {
247fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt#if GTEST_OS_WINDOWS
248fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  // TODO(wan@google.com): on Windows a network share like
249fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  // \\server\share can be a root directory, although it cannot be the
250fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  // current directory.  Handle this properly.
251fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  return pathname_.length() == 3 && IsAbsolutePath();
252fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt#else
253fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  return pathname_.length() == 1 && IsPathSeparator(pathname_.c_str()[0]);
254fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt#endif
255fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt}
256fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt
257fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// Returns true if pathname describes an absolute path.
258fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholtbool FilePath::IsAbsolutePath() const {
259fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  const char* const name = pathname_.c_str();
260fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt#if GTEST_OS_WINDOWS
261fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  return pathname_.length() >= 3 &&
262fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt     ((name[0] >= 'a' && name[0] <= 'z') ||
263fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt      (name[0] >= 'A' && name[0] <= 'Z')) &&
264fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt     name[1] == ':' &&
265fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt     IsPathSeparator(name[2]);
266fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt#else
267fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  return IsPathSeparator(name[0]);
268fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt#endif
269fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt}
270fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt
271fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// Returns a pathname for a file that does not currently exist. The pathname
272fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// will be directory/base_name.extension or
273fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// directory/base_name_<number>.extension if directory/base_name.extension
274fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// already exists. The number will be incremented until a pathname is found
275fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// that does not already exist.
276fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// Examples: 'dir/foo_test.xml' or 'dir/foo_test_1.xml'.
277fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// There could be a race condition if two or more processes are calling this
278fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// function at the same time -- they could both pick the same filename.
279fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric AnholtFilePath FilePath::GenerateUniqueFileName(const FilePath& directory,
280fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt                                          const FilePath& base_name,
281fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt                                          const char* extension) {
282fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  FilePath full_pathname;
283fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  int number = 0;
284fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  do {
285fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt    full_pathname.Set(MakeFileName(directory, base_name, number++, extension));
286fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  } while (full_pathname.FileOrDirectoryExists());
287fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  return full_pathname;
288fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt}
289fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt
290fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// Returns true if FilePath ends with a path separator, which indicates that
291fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// it is intended to represent a directory. Returns false otherwise.
292fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// This does NOT check that a directory (or file) actually exists.
293fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholtbool FilePath::IsDirectory() const {
294fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  return !pathname_.empty() &&
295fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt         IsPathSeparator(pathname_.c_str()[pathname_.length() - 1]);
296fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt}
297fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt
298fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// Create directories so that path exists. Returns true if successful or if
299fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// the directories already exist; returns false if unable to create directories
300fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// for any reason.
301fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholtbool FilePath::CreateDirectoriesRecursively() const {
302fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  if (!this->IsDirectory()) {
303fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt    return false;
304fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  }
305fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt
306fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  if (pathname_.length() == 0 || this->DirectoryExists()) {
307fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt    return true;
308fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  }
309fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt
310fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  const FilePath parent(this->RemoveTrailingPathSeparator().RemoveFileName());
311fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  return parent.CreateDirectoriesRecursively() && this->CreateFolder();
312fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt}
313fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt
314fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// Create the directory so that path exists. Returns true if successful or
315fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// if the directory already exists; returns false if unable to create the
316fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// directory for any reason, including if the parent directory does not
317fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// exist. Not named "CreateDirectory" because that's a macro on Windows.
318fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholtbool FilePath::CreateFolder() const {
319fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt#if GTEST_OS_WINDOWS_MOBILE
320fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  FilePath removed_sep(this->RemoveTrailingPathSeparator());
321fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  LPCWSTR unicode = String::AnsiToUtf16(removed_sep.c_str());
322fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  int result = CreateDirectory(unicode, NULL) ? 0 : -1;
323fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  delete [] unicode;
324fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt#elif GTEST_OS_WINDOWS
325fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  int result = _mkdir(pathname_.c_str());
326fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt#else
327fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  int result = mkdir(pathname_.c_str(), 0777);
328fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt#endif  // GTEST_OS_WINDOWS_MOBILE
329fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt
330fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  if (result == -1) {
331fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt    return this->DirectoryExists();  // An error is OK if the directory exists.
332fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  }
333fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  return true;  // No error.
334fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt}
335fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt
336fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// If input name has a trailing separator character, remove it and return the
337fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// name, otherwise return the name string unmodified.
338fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// On Windows platform, uses \ as the separator, other platforms use /.
339fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric AnholtFilePath FilePath::RemoveTrailingPathSeparator() const {
340fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  return IsDirectory()
341fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt      ? FilePath(String(pathname_.c_str(), pathname_.length() - 1))
342fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt      : *this;
343fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt}
344fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt
345fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// Removes any redundant separators that might be in the pathname.
346fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// For example, "bar///foo" becomes "bar/foo". Does not eliminate other
347fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// redundancies that might be in a pathname involving "." or "..".
348fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt// TODO(wan@google.com): handle Windows network shares (e.g. \\server\share).
349fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholtvoid FilePath::Normalize() {
350fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  if (pathname_.c_str() == NULL) {
351fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt    pathname_ = "";
352fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt    return;
353fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  }
354fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  const char* src = pathname_.c_str();
355fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  char* const dest = new char[pathname_.length() + 1];
356fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  char* dest_ptr = dest;
357fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  memset(dest_ptr, 0, pathname_.length() + 1);
358fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt
359fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  while (*src != '\0') {
360fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt    *dest_ptr = *src;
361fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt    if (!IsPathSeparator(*src)) {
362fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt      src++;
363fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt    } else {
364fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt#if GTEST_HAS_ALT_PATH_SEP_
365fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt      if (*dest_ptr == kAlternatePathSeparator) {
366fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt        *dest_ptr = kPathSeparator;
367fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt      }
368fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt#endif
369fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt      while (IsPathSeparator(*src))
370fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt        src++;
371fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt    }
372fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt    dest_ptr++;
373fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  }
374fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  *dest_ptr = '\0';
375fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  pathname_ = dest;
376fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt  delete[] dest;
377fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt}
378fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt
379fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt}  // namespace internal
380fe358c0ffa4acb7ecab10fda68ba9740497d2e7fEric Anholt}  // namespace testing
381