1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/ipc/chromium/src/base/file_path.cc Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,330 @@ 1.4 +// Copyright (c) 2008 The Chromium Authors. All rights reserved. 1.5 +// Use of this source code is governed by a BSD-style license that can be 1.6 +// found in the LICENSE file. 1.7 + 1.8 +#include <fstream> 1.9 + 1.10 +#include "base/file_path.h" 1.11 +#include "base/logging.h" 1.12 + 1.13 +// These includes are just for the *Hack functions, and should be removed 1.14 +// when those functions are removed. 1.15 +#include "base/string_piece.h" 1.16 +#include "base/string_util.h" 1.17 +#include "base/sys_string_conversions.h" 1.18 + 1.19 +#if defined(FILE_PATH_USES_WIN_SEPARATORS) 1.20 +const FilePath::CharType FilePath::kSeparators[] = FILE_PATH_LITERAL("\\/"); 1.21 +#else // FILE_PATH_USES_WIN_SEPARATORS 1.22 +const FilePath::CharType FilePath::kSeparators[] = FILE_PATH_LITERAL("/"); 1.23 +#endif // FILE_PATH_USES_WIN_SEPARATORS 1.24 + 1.25 +const FilePath::CharType FilePath::kCurrentDirectory[] = FILE_PATH_LITERAL("."); 1.26 +const FilePath::CharType FilePath::kParentDirectory[] = FILE_PATH_LITERAL(".."); 1.27 + 1.28 +const FilePath::CharType FilePath::kExtensionSeparator = FILE_PATH_LITERAL('.'); 1.29 + 1.30 + 1.31 +namespace { 1.32 + 1.33 +// If this FilePath contains a drive letter specification, returns the 1.34 +// position of the last character of the drive letter specification, 1.35 +// otherwise returns npos. This can only be true on Windows, when a pathname 1.36 +// begins with a letter followed by a colon. On other platforms, this always 1.37 +// returns npos. 1.38 +FilePath::StringType::size_type FindDriveLetter( 1.39 + const FilePath::StringType& path) { 1.40 +#if defined(FILE_PATH_USES_DRIVE_LETTERS) 1.41 + // This is dependent on an ASCII-based character set, but that's a 1.42 + // reasonable assumption. iswalpha can be too inclusive here. 1.43 + if (path.length() >= 2 && path[1] == L':' && 1.44 + ((path[0] >= L'A' && path[0] <= L'Z') || 1.45 + (path[0] >= L'a' && path[0] <= L'z'))) { 1.46 + return 1; 1.47 + } 1.48 +#endif // FILE_PATH_USES_DRIVE_LETTERS 1.49 + return FilePath::StringType::npos; 1.50 +} 1.51 + 1.52 +bool IsPathAbsolute(const FilePath::StringType& path) { 1.53 +#if defined(FILE_PATH_USES_DRIVE_LETTERS) 1.54 + FilePath::StringType::size_type letter = FindDriveLetter(path); 1.55 + if (letter != FilePath::StringType::npos) { 1.56 + // Look for a separator right after the drive specification. 1.57 + return path.length() > letter + 1 && 1.58 + FilePath::IsSeparator(path[letter + 1]); 1.59 + } 1.60 + // Look for a pair of leading separators. 1.61 + return path.length() > 1 && 1.62 + FilePath::IsSeparator(path[0]) && FilePath::IsSeparator(path[1]); 1.63 +#else // FILE_PATH_USES_DRIVE_LETTERS 1.64 + // Look for a separator in the first position. 1.65 + return path.length() > 0 && FilePath::IsSeparator(path[0]); 1.66 +#endif // FILE_PATH_USES_DRIVE_LETTERS 1.67 +} 1.68 + 1.69 +} // namespace 1.70 + 1.71 +bool FilePath::IsSeparator(CharType character) { 1.72 + for (size_t i = 0; i < arraysize(kSeparators) - 1; ++i) { 1.73 + if (character == kSeparators[i]) { 1.74 + return true; 1.75 + } 1.76 + } 1.77 + 1.78 + return false; 1.79 +} 1.80 + 1.81 +// libgen's dirname and basename aren't guaranteed to be thread-safe and aren't 1.82 +// guaranteed to not modify their input strings, and in fact are implemented 1.83 +// differently in this regard on different platforms. Don't use them, but 1.84 +// adhere to their behavior. 1.85 +FilePath FilePath::DirName() const { 1.86 + FilePath new_path(path_); 1.87 + new_path.StripTrailingSeparatorsInternal(); 1.88 + 1.89 + // The drive letter, if any, always needs to remain in the output. If there 1.90 + // is no drive letter, as will always be the case on platforms which do not 1.91 + // support drive letters, letter will be npos, or -1, so the comparisons and 1.92 + // resizes below using letter will still be valid. 1.93 + StringType::size_type letter = FindDriveLetter(new_path.path_); 1.94 + 1.95 + StringType::size_type last_separator = 1.96 + new_path.path_.find_last_of(kSeparators, StringType::npos, 1.97 + arraysize(kSeparators) - 1); 1.98 + if (last_separator == StringType::npos) { 1.99 + // path_ is in the current directory. 1.100 + new_path.path_.resize(letter + 1); 1.101 + } else if (last_separator == letter + 1) { 1.102 + // path_ is in the root directory. 1.103 + new_path.path_.resize(letter + 2); 1.104 + } else if (last_separator == letter + 2 && 1.105 + IsSeparator(new_path.path_[letter + 1])) { 1.106 + // path_ is in "//" (possibly with a drive letter); leave the double 1.107 + // separator intact indicating alternate root. 1.108 + new_path.path_.resize(letter + 3); 1.109 + } else if (last_separator != 0) { 1.110 + // path_ is somewhere else, trim the basename. 1.111 + new_path.path_.resize(last_separator); 1.112 + } 1.113 + 1.114 + new_path.StripTrailingSeparatorsInternal(); 1.115 + if (!new_path.path_.length()) 1.116 + new_path.path_ = kCurrentDirectory; 1.117 + 1.118 + return new_path; 1.119 +} 1.120 + 1.121 +FilePath FilePath::BaseName() const { 1.122 + FilePath new_path(path_); 1.123 + new_path.StripTrailingSeparatorsInternal(); 1.124 + 1.125 + // The drive letter, if any, is always stripped. 1.126 + StringType::size_type letter = FindDriveLetter(new_path.path_); 1.127 + if (letter != StringType::npos) { 1.128 + new_path.path_.erase(0, letter + 1); 1.129 + } 1.130 + 1.131 + // Keep everything after the final separator, but if the pathname is only 1.132 + // one character and it's a separator, leave it alone. 1.133 + StringType::size_type last_separator = 1.134 + new_path.path_.find_last_of(kSeparators, StringType::npos, 1.135 + arraysize(kSeparators) - 1); 1.136 + if (last_separator != StringType::npos && 1.137 + last_separator < new_path.path_.length() - 1) { 1.138 + new_path.path_.erase(0, last_separator + 1); 1.139 + } 1.140 + 1.141 + return new_path; 1.142 +} 1.143 + 1.144 +FilePath::StringType FilePath::Extension() const { 1.145 + // BaseName() calls StripTrailingSeparators, so cases like /foo.baz/// work. 1.146 + StringType base = BaseName().value(); 1.147 + 1.148 + // Special case "." and ".." 1.149 + if (base == kCurrentDirectory || base == kParentDirectory) 1.150 + return StringType(); 1.151 + 1.152 + const StringType::size_type last_dot = base.rfind(kExtensionSeparator); 1.153 + if (last_dot == StringType::npos) 1.154 + return StringType(); 1.155 + return StringType(base, last_dot); 1.156 +} 1.157 + 1.158 +FilePath FilePath::RemoveExtension() const { 1.159 + StringType ext = Extension(); 1.160 + // It's important to check Extension() since that verifies that the 1.161 + // kExtensionSeparator actually appeared in the last path component. 1.162 + if (ext.empty()) 1.163 + return FilePath(path_); 1.164 + // Since Extension() verified that the extension is in fact in the last path 1.165 + // component, this substr will effectively strip trailing separators. 1.166 + const StringType::size_type last_dot = path_.rfind(kExtensionSeparator); 1.167 + return FilePath(path_.substr(0, last_dot)); 1.168 +} 1.169 + 1.170 +FilePath FilePath::InsertBeforeExtension(const StringType& suffix) const { 1.171 + if (suffix.empty()) 1.172 + return FilePath(path_); 1.173 + 1.174 + if (path_.empty()) 1.175 + return FilePath(); 1.176 + 1.177 + StringType base = BaseName().value(); 1.178 + if (base.empty()) 1.179 + return FilePath(); 1.180 + if (*(base.end() - 1) == kExtensionSeparator) { 1.181 + // Special case "." and ".." 1.182 + if (base == kCurrentDirectory || base == kParentDirectory) { 1.183 + return FilePath(); 1.184 + } 1.185 + } 1.186 + 1.187 + StringType ext = Extension(); 1.188 + StringType ret = RemoveExtension().value(); 1.189 + ret.append(suffix); 1.190 + ret.append(ext); 1.191 + return FilePath(ret); 1.192 +} 1.193 + 1.194 +FilePath FilePath::ReplaceExtension(const StringType& extension) const { 1.195 + if (path_.empty()) 1.196 + return FilePath(); 1.197 + 1.198 + StringType base = BaseName().value(); 1.199 + if (base.empty()) 1.200 + return FilePath(); 1.201 + if (*(base.end() - 1) == kExtensionSeparator) { 1.202 + // Special case "." and ".." 1.203 + if (base == kCurrentDirectory || base == kParentDirectory) { 1.204 + return FilePath(); 1.205 + } 1.206 + } 1.207 + 1.208 + FilePath no_ext = RemoveExtension(); 1.209 + // If the new extension is "" or ".", then just remove the current extension. 1.210 + if (extension.empty() || extension == StringType(1, kExtensionSeparator)) 1.211 + return no_ext; 1.212 + 1.213 + StringType str = no_ext.value(); 1.214 + if (extension[0] != kExtensionSeparator) 1.215 + str.append(1, kExtensionSeparator); 1.216 + str.append(extension); 1.217 + return FilePath(str); 1.218 +} 1.219 + 1.220 +FilePath FilePath::Append(const StringType& component) const { 1.221 + DCHECK(!IsPathAbsolute(component)); 1.222 + if (path_.compare(kCurrentDirectory) == 0) { 1.223 + // Append normally doesn't do any normalization, but as a special case, 1.224 + // when appending to kCurrentDirectory, just return a new path for the 1.225 + // component argument. Appending component to kCurrentDirectory would 1.226 + // serve no purpose other than needlessly lengthening the path, and 1.227 + // it's likely in practice to wind up with FilePath objects containing 1.228 + // only kCurrentDirectory when calling DirName on a single relative path 1.229 + // component. 1.230 + return FilePath(component); 1.231 + } 1.232 + 1.233 + FilePath new_path(path_); 1.234 + new_path.StripTrailingSeparatorsInternal(); 1.235 + 1.236 + // Don't append a separator if the path is empty (indicating the current 1.237 + // directory) or if the path component is empty (indicating nothing to 1.238 + // append). 1.239 + if (component.length() > 0 && new_path.path_.length() > 0) { 1.240 + 1.241 + // Don't append a separator if the path still ends with a trailing 1.242 + // separator after stripping (indicating the root directory). 1.243 + if (!IsSeparator(new_path.path_[new_path.path_.length() - 1])) { 1.244 + 1.245 + // Don't append a separator if the path is just a drive letter. 1.246 + if (FindDriveLetter(new_path.path_) + 1 != new_path.path_.length()) { 1.247 + new_path.path_.append(1, kSeparators[0]); 1.248 + } 1.249 + } 1.250 + } 1.251 + 1.252 + new_path.path_.append(component); 1.253 + return new_path; 1.254 +} 1.255 + 1.256 +FilePath FilePath::Append(const FilePath& component) const { 1.257 + return Append(component.value()); 1.258 +} 1.259 + 1.260 +FilePath FilePath::AppendASCII(const std::string& component) const { 1.261 + DCHECK(IsStringASCII(component)); 1.262 +#if defined(OS_WIN) 1.263 + return Append(ASCIIToWide(component)); 1.264 +#elif defined(OS_POSIX) 1.265 + return Append(component); 1.266 +#endif 1.267 +} 1.268 + 1.269 +bool FilePath::IsAbsolute() const { 1.270 + return IsPathAbsolute(path_); 1.271 +} 1.272 + 1.273 +#if defined(OS_POSIX) 1.274 +// See file_path.h for a discussion of the encoding of paths on POSIX 1.275 +// platforms. These *Hack() functions are not quite correct, but they're 1.276 +// only temporary while we fix the remainder of the code. 1.277 +// Remember to remove the #includes at the top when you remove these. 1.278 + 1.279 +// static 1.280 +FilePath FilePath::FromWStringHack(const std::wstring& wstring) { 1.281 + return FilePath(base::SysWideToNativeMB(wstring)); 1.282 +} 1.283 +std::wstring FilePath::ToWStringHack() const { 1.284 + return base::SysNativeMBToWide(path_); 1.285 +} 1.286 +#elif defined(OS_WIN) 1.287 +// static 1.288 +FilePath FilePath::FromWStringHack(const std::wstring& wstring) { 1.289 + return FilePath(wstring); 1.290 +} 1.291 +std::wstring FilePath::ToWStringHack() const { 1.292 + return path_; 1.293 +} 1.294 +#endif 1.295 + 1.296 +void FilePath::OpenInputStream(std::ifstream& stream) const { 1.297 + stream.open( 1.298 +#ifndef __MINGW32__ 1.299 + path_.c_str(), 1.300 +#else 1.301 + base::SysWideToNativeMB(path_).c_str(), 1.302 +#endif 1.303 + std::ios::in | std::ios::binary); 1.304 +} 1.305 + 1.306 +FilePath FilePath::StripTrailingSeparators() const { 1.307 + FilePath new_path(path_); 1.308 + new_path.StripTrailingSeparatorsInternal(); 1.309 + 1.310 + return new_path; 1.311 +} 1.312 + 1.313 +void FilePath::StripTrailingSeparatorsInternal() { 1.314 + // If there is no drive letter, start will be 1, which will prevent stripping 1.315 + // the leading separator if there is only one separator. If there is a drive 1.316 + // letter, start will be set appropriately to prevent stripping the first 1.317 + // separator following the drive letter, if a separator immediately follows 1.318 + // the drive letter. 1.319 + StringType::size_type start = FindDriveLetter(path_) + 2; 1.320 + 1.321 + StringType::size_type last_stripped = StringType::npos; 1.322 + for (StringType::size_type pos = path_.length(); 1.323 + pos > start && IsSeparator(path_[pos - 1]); 1.324 + --pos) { 1.325 + // If the string only has two separators and they're at the beginning, 1.326 + // don't strip them, unless the string began with more than two separators. 1.327 + if (pos != start + 1 || last_stripped == start + 2 || 1.328 + !IsSeparator(path_[start - 1])) { 1.329 + path_.resize(pos - 1); 1.330 + last_stripped = pos; 1.331 + } 1.332 + } 1.333 +}