Lingfeng Yang | c02cb03 | 2020-10-26 14:21:25 -0700 | [diff] [blame] | 1 | // Copyright 2020 The Android Open Source Project |
| 2 | // |
| 3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | // you may not use this file except in compliance with the License. |
| 5 | // You may obtain a copy of the License at |
| 6 | // |
| 7 | // http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | // |
| 9 | // Unless required by applicable law or agreed to in writing, software |
| 10 | // distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | // See the License for the specific language governing permissions and |
| 13 | // limitations under the License. |
| 14 | |
| 15 | #pragma once |
| 16 | |
| 17 | #include <stddef.h> // for size_t |
| 18 | #include <string> // for string, basic_string |
| 19 | #include <utility> // for move, forward |
| 20 | #include <vector> // for vector |
| 21 | |
| 22 | #include "base/Optional.h" // for Optional |
| 23 | |
| 24 | #ifdef __APPLE__ |
| 25 | |
| 26 | #define LIBSUFFIX ".dylib" |
| 27 | |
| 28 | #else |
| 29 | |
| 30 | #ifdef _WIN32 |
| 31 | #include "base/Win32UnicodeString.h" |
| 32 | #define LIBSUFFIX ".dll" |
| 33 | |
| 34 | #else |
| 35 | |
| 36 | #define LIBSUFFIX ".so" |
| 37 | |
| 38 | #endif // !_WIN32 (linux) |
| 39 | |
| 40 | #endif // !__APPLE__ |
| 41 | |
| 42 | namespace android { |
| 43 | namespace base { |
| 44 | |
| 45 | // Utility functions to manage file paths. None of these should touch the |
| 46 | // file system. All methods must be static. |
| 47 | class PathUtils { |
| 48 | public: |
| 49 | // An enum listing the supported host file system types. |
| 50 | // HOST_POSIX means a Posix-like file system. |
| 51 | // HOST_WIN32 means a Windows-like file system. |
| 52 | // HOST_TYPE means the current host type (one of the above). |
| 53 | // NOTE: If you update this list, modify kHostTypeCount below too. |
| 54 | enum HostType { |
| 55 | HOST_POSIX = 0, |
| 56 | HOST_WIN32 = 1, |
| 57 | #ifdef _WIN32 |
| 58 | HOST_TYPE = HOST_WIN32, |
| 59 | #else |
| 60 | HOST_TYPE = HOST_POSIX, |
| 61 | #endif |
| 62 | }; |
| 63 | |
| 64 | // The number of distinct items in the HostType enumeration above. |
| 65 | static const int kHostTypeCount = 2; |
| 66 | |
| 67 | // Suffixes for an executable file (.exe on Windows, empty otherwise) |
| 68 | static const char* const kExeNameSuffixes[kHostTypeCount]; |
| 69 | |
| 70 | // Suffixe for an executable file on the current platform |
| 71 | static const char* const kExeNameSuffix; |
| 72 | |
| 73 | // Returns the executable name for a base name |baseName| |
| 74 | static std::string toExecutableName(const char* baseName, HostType hostType); |
| 75 | |
| 76 | static std::string toExecutableName(const char* baseName) { |
| 77 | return toExecutableName(baseName, HOST_TYPE); |
| 78 | } |
| 79 | |
| 80 | // Return true if |ch| is a directory separator for a given |hostType|. |
| 81 | static bool isDirSeparator(int ch, HostType hostType); |
| 82 | |
| 83 | // Return true if |ch| is a directory separator for the current platform. |
| 84 | static bool isDirSeparator(int ch) { |
| 85 | return isDirSeparator(ch, HOST_TYPE); |
| 86 | } |
| 87 | |
| 88 | // Return true if |ch| is a path separator for a given |hostType|. |
| 89 | static bool isPathSeparator(int ch, HostType hostType); |
| 90 | |
| 91 | // Return true if |ch| is a path separator for the current platform. |
| 92 | static bool isPathSeparator(int ch) { |
| 93 | return isPathSeparator(ch, HOST_TYPE); |
| 94 | } |
| 95 | |
| 96 | // Return the directory separator character for a given |hostType| |
| 97 | static char getDirSeparator(HostType hostType) { |
| 98 | return (hostType == HOST_WIN32) ? '\\' : '/'; |
| 99 | } |
| 100 | |
| 101 | // Remove trailing separators from a |path| string, for a given |hostType|. |
| 102 | static std::string removeTrailingDirSeparator(const char* path, |
| 103 | HostType hostType); |
| 104 | |
| 105 | // Remove trailing separators from a |path| string for the current host. |
| 106 | static std::string removeTrailingDirSeparator(const char* path) { |
| 107 | return removeTrailingDirSeparator(path, HOST_TYPE); |
| 108 | } |
| 109 | |
| 110 | // Add a trailing separator if needed. |
Lingfeng Yang | 0e6868f | 2020-11-03 12:32:59 -0800 | [diff] [blame] | 111 | static std::string addTrailingDirSeparator(const std::string& path, |
| 112 | HostType hostType); |
Lingfeng Yang | c02cb03 | 2020-10-26 14:21:25 -0700 | [diff] [blame] | 113 | static std::string addTrailingDirSeparator(const char* path, |
| 114 | HostType hostType); |
| 115 | |
| 116 | // Add a trailing separator if needed. |
Lingfeng Yang | 0e6868f | 2020-11-03 12:32:59 -0800 | [diff] [blame] | 117 | static std::string addTrailingDirSeparator(const std::string& path) { |
Lingfeng Yang | c02cb03 | 2020-10-26 14:21:25 -0700 | [diff] [blame] | 118 | return addTrailingDirSeparator(path, HOST_TYPE); |
| 119 | } |
| 120 | |
| 121 | // If |path| starts with a root prefix, return its size in bytes, or |
| 122 | // 0 otherwise. The definition of valid root prefixes depends on the |
| 123 | // value of |hostType|. For HOST_POSIX, it's any path that begins |
| 124 | // with a slash (/). For HOST_WIN32, the following prefixes are |
| 125 | // recognized: |
| 126 | // <drive>: |
| 127 | // <drive>:<sep> |
| 128 | // <sep><sep>volumeName<sep> |
| 129 | static size_t rootPrefixSize(const std::string& path, HostType hostType); |
| 130 | |
| 131 | // Return the root prefix for the current platform. See above for |
| 132 | // documentation. |
| 133 | static size_t rootPrefixSize(const char* path) { |
| 134 | return rootPrefixSize(path, HOST_TYPE); |
| 135 | } |
| 136 | |
| 137 | // Return true iff |path| is an absolute path for a given |hostType|. |
| 138 | static bool isAbsolute(const char* path, HostType hostType); |
| 139 | |
| 140 | // Return true iff |path| is an absolute path for the current host. |
| 141 | static bool isAbsolute(const char* path) { |
| 142 | return isAbsolute(path, HOST_TYPE); |
| 143 | } |
| 144 | |
| 145 | // Split |path| into a directory name and a file name. |dirName| and |
| 146 | // |baseName| are optional pointers to strings that will receive the |
| 147 | // corresponding components on success. |hostType| is a host type. |
| 148 | // Return true on success, or false on failure. |
| 149 | // Note that unlike the Unix 'basename' command, the command will fail |
| 150 | // if |path| ends with directory separator or is a single root prefix. |
| 151 | // Windows root prefixes are fully supported, which means the following: |
| 152 | // |
| 153 | // / -> error. |
| 154 | // /foo -> '/' + 'foo' |
| 155 | // foo -> '.' + 'foo' |
| 156 | // <drive>: -> error. |
| 157 | // <drive>:foo -> '<drive>:' + 'foo' |
| 158 | // <drive>:\foo -> '<drive>:\' + 'foo' |
| 159 | // |
| 160 | static bool split(const char* path, |
| 161 | HostType hostType, |
| 162 | const char** dirName, |
| 163 | const char** baseName); |
| 164 | |
| 165 | // A variant of split() for the current process' host type. |
| 166 | static bool split(const char* path, |
| 167 | const char** dirName, |
| 168 | const char** baseName) { |
| 169 | return split(path, HOST_TYPE, dirName, baseName); |
| 170 | } |
| 171 | |
| 172 | // Join two path components together. Note that if |path2| is an |
| 173 | // absolute path, this function returns a copy of |path2|, otherwise |
| 174 | // the result will be the concatenation of |path1| and |path2|, if |
| 175 | // |path1| doesn't end with a directory separator, a |hostType| specific |
| 176 | // one will be inserted between the two paths in the result. |
| 177 | static std::string join(const std::string& path1, |
| 178 | const std::string& path2, |
| 179 | HostType hostType); |
| 180 | |
| 181 | // A variant of join() for the current process' host type. |
| 182 | static std::string join(const std::string& path1, const std::string& path2) { |
| 183 | return join(path1, path2, HOST_TYPE); |
| 184 | } |
| 185 | |
| 186 | // A convenience function to join a bunch of paths at once |
| 187 | template <class... Paths> |
| 188 | static std::string join(const std::string& path1, |
| 189 | const std::string& path2, |
| 190 | Paths&&... paths) { |
| 191 | return join(path1, join(path2, std::forward<Paths>(paths)...)); |
| 192 | } |
| 193 | |
| 194 | // Decompose |path| into individual components. If |path| has a root |
| 195 | // prefix, it will always be the first component. I.e. for Posix |
| 196 | // systems this will be '/' (for absolute paths). For Win32 systems, |
| 197 | // it could be 'C:" (for a path relative to a root volume) or "C:\" |
| 198 | // for an absolute path from volume C)., |
| 199 | // On success, return true and sets |out| to a vector of strings, |
| 200 | // each one being a path component (prefix or subdirectory or file |
| 201 | // name). Directory separators do not appear in components, except |
| 202 | // for the root prefix, if any. |
| 203 | static std::vector<std::string> decompose(std::string&& path, |
| 204 | HostType hostType); |
Lingfeng Yang | 0e6868f | 2020-11-03 12:32:59 -0800 | [diff] [blame] | 205 | static std::vector<std::string> decompose(const std::string& path, |
| 206 | HostType hostType); |
Lingfeng Yang | c02cb03 | 2020-10-26 14:21:25 -0700 | [diff] [blame] | 207 | |
| 208 | template <class String> |
| 209 | static std::vector<String> decompose(const String& path, |
| 210 | HostType hostType); |
| 211 | |
| 212 | // Decompose |path| into individual components for the host platform. |
| 213 | // See comments above for more details. |
| 214 | static std::vector<std::string> decompose(std::string&& path) { |
| 215 | return decompose(std::move(path), HOST_TYPE); |
| 216 | } |
| 217 | |
Lingfeng Yang | 0e6868f | 2020-11-03 12:32:59 -0800 | [diff] [blame] | 218 | static std::vector<std::string> decompose(const std::string& path) { |
| 219 | return decompose(path, HOST_TYPE); |
| 220 | } |
| 221 | |
Lingfeng Yang | c02cb03 | 2020-10-26 14:21:25 -0700 | [diff] [blame] | 222 | // Recompose a path from individual components into a file path string. |
| 223 | // |components| is a vector of strings, and |hostType| the target |
| 224 | // host type to use. Return a new file path string. Note that if the |
| 225 | // first component is a root prefix, it will be kept as is, i.e.: |
| 226 | // [ 'C:', 'foo' ] -> 'C:foo' on Win32, but not Posix where it will |
| 227 | // be 'C:/foo'. |
| 228 | static std::string recompose(const std::vector<std::string>& components, |
| 229 | HostType hostType); |
| 230 | template <class String> |
| 231 | static std::string recompose(const std::vector<String>& components, |
| 232 | HostType hostType); |
| 233 | |
| 234 | // Recompose a path from individual components into a file path string |
| 235 | // for the current host. |components| is a vector os strings. |
| 236 | // Returns a new file path string. |
| 237 | template <class String> |
| 238 | static std::string recompose(const std::vector<String>& components) { |
| 239 | return PathUtils::recompose(components, HOST_TYPE); |
| 240 | } |
| 241 | |
| 242 | // Given a list of components returned by decompose(), simplify it |
| 243 | // by removing instances of '.' and '..' when that makes sense. |
| 244 | // Note that it is not possible to simplify initial instances of |
| 245 | // '..', i.e. "foo/../../bar" -> "../bar" |
| 246 | static void simplifyComponents(std::vector<std::string>* components); |
| 247 | template <class String> |
| 248 | static void simplifyComponents(std::vector<String>* components); |
| 249 | |
| 250 | // Returns a version of |path| that is relative to |base|. |
| 251 | // This can be useful for converting absolute paths to |
| 252 | // relative paths given |base|. |
| 253 | // Example: |
| 254 | // |base|: C:\Users\foo |
| 255 | // |path|: C:\Users\foo\AppData\Local\Android\Sdk |
| 256 | // would give |
| 257 | // AppData\Local\Android\Sdk. |
| 258 | // If |base| is not a prefix of |path|, fails by returning |
| 259 | // the original |path| unmodified. |
| 260 | static std::string relativeTo(const char* base, const char* path, HostType hostType); |
| 261 | static std::string relativeTo(const char* base, const char* path) { |
| 262 | return relativeTo(base, path, HOST_TYPE); |
| 263 | } |
| 264 | |
| 265 | static Optional<std::string> pathWithoutDirs(const char* name); |
| 266 | static Optional<std::string> pathToDir(const char* name); |
| 267 | |
| 268 | // Replaces the entries ${xx} with the value of the environment variable |
| 269 | // xx if it exists. Returns kNullopt if the environment variable is |
| 270 | // not set or empty. |
| 271 | static Optional<std::string> pathWithEnvSubstituted(const char* path); |
| 272 | |
| 273 | // Replaces the entries ${xx} with the value of the environment variable |
| 274 | // xx if it exists. Returns kNullopt if the environment variable is |
| 275 | // not set or empty. |
| 276 | static Optional<std::string> pathWithEnvSubstituted(std::vector<std::string> decomposedPath); |
| 277 | |
| 278 | #ifdef _WIN32 |
| 279 | static Win32UnicodeString asUnicodePath(const char* path) { return Win32UnicodeString(path); } |
| 280 | #else |
| 281 | static std::string asUnicodePath(const char* path) { return path; } |
| 282 | #endif |
| 283 | }; |
| 284 | |
| 285 | // Useful shortcuts to avoid too much typing. |
| 286 | static const PathUtils::HostType kHostPosix = PathUtils::HOST_POSIX; |
| 287 | static const PathUtils::HostType kHostWin32 = PathUtils::HOST_WIN32; |
| 288 | static const PathUtils::HostType kHostType = PathUtils::HOST_TYPE; |
| 289 | |
| 290 | template <class... Paths> |
| 291 | std::string pj(const std::string& path1, |
| 292 | const std::string& path2, |
| 293 | Paths&&... paths) { |
| 294 | return PathUtils::join(path1, |
| 295 | pj(path2, std::forward<Paths>(paths)...)); |
| 296 | } |
| 297 | |
| 298 | std::string pj(const std::string& path1, const std::string& path2); |
| 299 | |
| 300 | std::string pj(const std::vector<std::string>& paths); |
| 301 | |
| 302 | bool pathExists(const char* path); |
| 303 | |
| 304 | } // namespace base |
| 305 | } // namespace android |