Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | // Copyright (c) 2008 The Chromium Authors. All rights reserved. |
michael@0 | 2 | // Use of this source code is governed by a BSD-style license that can be |
michael@0 | 3 | // found in the LICENSE file. |
michael@0 | 4 | |
michael@0 | 5 | #include <fstream> |
michael@0 | 6 | |
michael@0 | 7 | #include "base/file_path.h" |
michael@0 | 8 | #include "base/logging.h" |
michael@0 | 9 | |
michael@0 | 10 | // These includes are just for the *Hack functions, and should be removed |
michael@0 | 11 | // when those functions are removed. |
michael@0 | 12 | #include "base/string_piece.h" |
michael@0 | 13 | #include "base/string_util.h" |
michael@0 | 14 | #include "base/sys_string_conversions.h" |
michael@0 | 15 | |
michael@0 | 16 | #if defined(FILE_PATH_USES_WIN_SEPARATORS) |
michael@0 | 17 | const FilePath::CharType FilePath::kSeparators[] = FILE_PATH_LITERAL("\\/"); |
michael@0 | 18 | #else // FILE_PATH_USES_WIN_SEPARATORS |
michael@0 | 19 | const FilePath::CharType FilePath::kSeparators[] = FILE_PATH_LITERAL("/"); |
michael@0 | 20 | #endif // FILE_PATH_USES_WIN_SEPARATORS |
michael@0 | 21 | |
michael@0 | 22 | const FilePath::CharType FilePath::kCurrentDirectory[] = FILE_PATH_LITERAL("."); |
michael@0 | 23 | const FilePath::CharType FilePath::kParentDirectory[] = FILE_PATH_LITERAL(".."); |
michael@0 | 24 | |
michael@0 | 25 | const FilePath::CharType FilePath::kExtensionSeparator = FILE_PATH_LITERAL('.'); |
michael@0 | 26 | |
michael@0 | 27 | |
michael@0 | 28 | namespace { |
michael@0 | 29 | |
michael@0 | 30 | // If this FilePath contains a drive letter specification, returns the |
michael@0 | 31 | // position of the last character of the drive letter specification, |
michael@0 | 32 | // otherwise returns npos. This can only be true on Windows, when a pathname |
michael@0 | 33 | // begins with a letter followed by a colon. On other platforms, this always |
michael@0 | 34 | // returns npos. |
michael@0 | 35 | FilePath::StringType::size_type FindDriveLetter( |
michael@0 | 36 | const FilePath::StringType& path) { |
michael@0 | 37 | #if defined(FILE_PATH_USES_DRIVE_LETTERS) |
michael@0 | 38 | // This is dependent on an ASCII-based character set, but that's a |
michael@0 | 39 | // reasonable assumption. iswalpha can be too inclusive here. |
michael@0 | 40 | if (path.length() >= 2 && path[1] == L':' && |
michael@0 | 41 | ((path[0] >= L'A' && path[0] <= L'Z') || |
michael@0 | 42 | (path[0] >= L'a' && path[0] <= L'z'))) { |
michael@0 | 43 | return 1; |
michael@0 | 44 | } |
michael@0 | 45 | #endif // FILE_PATH_USES_DRIVE_LETTERS |
michael@0 | 46 | return FilePath::StringType::npos; |
michael@0 | 47 | } |
michael@0 | 48 | |
michael@0 | 49 | bool IsPathAbsolute(const FilePath::StringType& path) { |
michael@0 | 50 | #if defined(FILE_PATH_USES_DRIVE_LETTERS) |
michael@0 | 51 | FilePath::StringType::size_type letter = FindDriveLetter(path); |
michael@0 | 52 | if (letter != FilePath::StringType::npos) { |
michael@0 | 53 | // Look for a separator right after the drive specification. |
michael@0 | 54 | return path.length() > letter + 1 && |
michael@0 | 55 | FilePath::IsSeparator(path[letter + 1]); |
michael@0 | 56 | } |
michael@0 | 57 | // Look for a pair of leading separators. |
michael@0 | 58 | return path.length() > 1 && |
michael@0 | 59 | FilePath::IsSeparator(path[0]) && FilePath::IsSeparator(path[1]); |
michael@0 | 60 | #else // FILE_PATH_USES_DRIVE_LETTERS |
michael@0 | 61 | // Look for a separator in the first position. |
michael@0 | 62 | return path.length() > 0 && FilePath::IsSeparator(path[0]); |
michael@0 | 63 | #endif // FILE_PATH_USES_DRIVE_LETTERS |
michael@0 | 64 | } |
michael@0 | 65 | |
michael@0 | 66 | } // namespace |
michael@0 | 67 | |
michael@0 | 68 | bool FilePath::IsSeparator(CharType character) { |
michael@0 | 69 | for (size_t i = 0; i < arraysize(kSeparators) - 1; ++i) { |
michael@0 | 70 | if (character == kSeparators[i]) { |
michael@0 | 71 | return true; |
michael@0 | 72 | } |
michael@0 | 73 | } |
michael@0 | 74 | |
michael@0 | 75 | return false; |
michael@0 | 76 | } |
michael@0 | 77 | |
michael@0 | 78 | // libgen's dirname and basename aren't guaranteed to be thread-safe and aren't |
michael@0 | 79 | // guaranteed to not modify their input strings, and in fact are implemented |
michael@0 | 80 | // differently in this regard on different platforms. Don't use them, but |
michael@0 | 81 | // adhere to their behavior. |
michael@0 | 82 | FilePath FilePath::DirName() const { |
michael@0 | 83 | FilePath new_path(path_); |
michael@0 | 84 | new_path.StripTrailingSeparatorsInternal(); |
michael@0 | 85 | |
michael@0 | 86 | // The drive letter, if any, always needs to remain in the output. If there |
michael@0 | 87 | // is no drive letter, as will always be the case on platforms which do not |
michael@0 | 88 | // support drive letters, letter will be npos, or -1, so the comparisons and |
michael@0 | 89 | // resizes below using letter will still be valid. |
michael@0 | 90 | StringType::size_type letter = FindDriveLetter(new_path.path_); |
michael@0 | 91 | |
michael@0 | 92 | StringType::size_type last_separator = |
michael@0 | 93 | new_path.path_.find_last_of(kSeparators, StringType::npos, |
michael@0 | 94 | arraysize(kSeparators) - 1); |
michael@0 | 95 | if (last_separator == StringType::npos) { |
michael@0 | 96 | // path_ is in the current directory. |
michael@0 | 97 | new_path.path_.resize(letter + 1); |
michael@0 | 98 | } else if (last_separator == letter + 1) { |
michael@0 | 99 | // path_ is in the root directory. |
michael@0 | 100 | new_path.path_.resize(letter + 2); |
michael@0 | 101 | } else if (last_separator == letter + 2 && |
michael@0 | 102 | IsSeparator(new_path.path_[letter + 1])) { |
michael@0 | 103 | // path_ is in "//" (possibly with a drive letter); leave the double |
michael@0 | 104 | // separator intact indicating alternate root. |
michael@0 | 105 | new_path.path_.resize(letter + 3); |
michael@0 | 106 | } else if (last_separator != 0) { |
michael@0 | 107 | // path_ is somewhere else, trim the basename. |
michael@0 | 108 | new_path.path_.resize(last_separator); |
michael@0 | 109 | } |
michael@0 | 110 | |
michael@0 | 111 | new_path.StripTrailingSeparatorsInternal(); |
michael@0 | 112 | if (!new_path.path_.length()) |
michael@0 | 113 | new_path.path_ = kCurrentDirectory; |
michael@0 | 114 | |
michael@0 | 115 | return new_path; |
michael@0 | 116 | } |
michael@0 | 117 | |
michael@0 | 118 | FilePath FilePath::BaseName() const { |
michael@0 | 119 | FilePath new_path(path_); |
michael@0 | 120 | new_path.StripTrailingSeparatorsInternal(); |
michael@0 | 121 | |
michael@0 | 122 | // The drive letter, if any, is always stripped. |
michael@0 | 123 | StringType::size_type letter = FindDriveLetter(new_path.path_); |
michael@0 | 124 | if (letter != StringType::npos) { |
michael@0 | 125 | new_path.path_.erase(0, letter + 1); |
michael@0 | 126 | } |
michael@0 | 127 | |
michael@0 | 128 | // Keep everything after the final separator, but if the pathname is only |
michael@0 | 129 | // one character and it's a separator, leave it alone. |
michael@0 | 130 | StringType::size_type last_separator = |
michael@0 | 131 | new_path.path_.find_last_of(kSeparators, StringType::npos, |
michael@0 | 132 | arraysize(kSeparators) - 1); |
michael@0 | 133 | if (last_separator != StringType::npos && |
michael@0 | 134 | last_separator < new_path.path_.length() - 1) { |
michael@0 | 135 | new_path.path_.erase(0, last_separator + 1); |
michael@0 | 136 | } |
michael@0 | 137 | |
michael@0 | 138 | return new_path; |
michael@0 | 139 | } |
michael@0 | 140 | |
michael@0 | 141 | FilePath::StringType FilePath::Extension() const { |
michael@0 | 142 | // BaseName() calls StripTrailingSeparators, so cases like /foo.baz/// work. |
michael@0 | 143 | StringType base = BaseName().value(); |
michael@0 | 144 | |
michael@0 | 145 | // Special case "." and ".." |
michael@0 | 146 | if (base == kCurrentDirectory || base == kParentDirectory) |
michael@0 | 147 | return StringType(); |
michael@0 | 148 | |
michael@0 | 149 | const StringType::size_type last_dot = base.rfind(kExtensionSeparator); |
michael@0 | 150 | if (last_dot == StringType::npos) |
michael@0 | 151 | return StringType(); |
michael@0 | 152 | return StringType(base, last_dot); |
michael@0 | 153 | } |
michael@0 | 154 | |
michael@0 | 155 | FilePath FilePath::RemoveExtension() const { |
michael@0 | 156 | StringType ext = Extension(); |
michael@0 | 157 | // It's important to check Extension() since that verifies that the |
michael@0 | 158 | // kExtensionSeparator actually appeared in the last path component. |
michael@0 | 159 | if (ext.empty()) |
michael@0 | 160 | return FilePath(path_); |
michael@0 | 161 | // Since Extension() verified that the extension is in fact in the last path |
michael@0 | 162 | // component, this substr will effectively strip trailing separators. |
michael@0 | 163 | const StringType::size_type last_dot = path_.rfind(kExtensionSeparator); |
michael@0 | 164 | return FilePath(path_.substr(0, last_dot)); |
michael@0 | 165 | } |
michael@0 | 166 | |
michael@0 | 167 | FilePath FilePath::InsertBeforeExtension(const StringType& suffix) const { |
michael@0 | 168 | if (suffix.empty()) |
michael@0 | 169 | return FilePath(path_); |
michael@0 | 170 | |
michael@0 | 171 | if (path_.empty()) |
michael@0 | 172 | return FilePath(); |
michael@0 | 173 | |
michael@0 | 174 | StringType base = BaseName().value(); |
michael@0 | 175 | if (base.empty()) |
michael@0 | 176 | return FilePath(); |
michael@0 | 177 | if (*(base.end() - 1) == kExtensionSeparator) { |
michael@0 | 178 | // Special case "." and ".." |
michael@0 | 179 | if (base == kCurrentDirectory || base == kParentDirectory) { |
michael@0 | 180 | return FilePath(); |
michael@0 | 181 | } |
michael@0 | 182 | } |
michael@0 | 183 | |
michael@0 | 184 | StringType ext = Extension(); |
michael@0 | 185 | StringType ret = RemoveExtension().value(); |
michael@0 | 186 | ret.append(suffix); |
michael@0 | 187 | ret.append(ext); |
michael@0 | 188 | return FilePath(ret); |
michael@0 | 189 | } |
michael@0 | 190 | |
michael@0 | 191 | FilePath FilePath::ReplaceExtension(const StringType& extension) const { |
michael@0 | 192 | if (path_.empty()) |
michael@0 | 193 | return FilePath(); |
michael@0 | 194 | |
michael@0 | 195 | StringType base = BaseName().value(); |
michael@0 | 196 | if (base.empty()) |
michael@0 | 197 | return FilePath(); |
michael@0 | 198 | if (*(base.end() - 1) == kExtensionSeparator) { |
michael@0 | 199 | // Special case "." and ".." |
michael@0 | 200 | if (base == kCurrentDirectory || base == kParentDirectory) { |
michael@0 | 201 | return FilePath(); |
michael@0 | 202 | } |
michael@0 | 203 | } |
michael@0 | 204 | |
michael@0 | 205 | FilePath no_ext = RemoveExtension(); |
michael@0 | 206 | // If the new extension is "" or ".", then just remove the current extension. |
michael@0 | 207 | if (extension.empty() || extension == StringType(1, kExtensionSeparator)) |
michael@0 | 208 | return no_ext; |
michael@0 | 209 | |
michael@0 | 210 | StringType str = no_ext.value(); |
michael@0 | 211 | if (extension[0] != kExtensionSeparator) |
michael@0 | 212 | str.append(1, kExtensionSeparator); |
michael@0 | 213 | str.append(extension); |
michael@0 | 214 | return FilePath(str); |
michael@0 | 215 | } |
michael@0 | 216 | |
michael@0 | 217 | FilePath FilePath::Append(const StringType& component) const { |
michael@0 | 218 | DCHECK(!IsPathAbsolute(component)); |
michael@0 | 219 | if (path_.compare(kCurrentDirectory) == 0) { |
michael@0 | 220 | // Append normally doesn't do any normalization, but as a special case, |
michael@0 | 221 | // when appending to kCurrentDirectory, just return a new path for the |
michael@0 | 222 | // component argument. Appending component to kCurrentDirectory would |
michael@0 | 223 | // serve no purpose other than needlessly lengthening the path, and |
michael@0 | 224 | // it's likely in practice to wind up with FilePath objects containing |
michael@0 | 225 | // only kCurrentDirectory when calling DirName on a single relative path |
michael@0 | 226 | // component. |
michael@0 | 227 | return FilePath(component); |
michael@0 | 228 | } |
michael@0 | 229 | |
michael@0 | 230 | FilePath new_path(path_); |
michael@0 | 231 | new_path.StripTrailingSeparatorsInternal(); |
michael@0 | 232 | |
michael@0 | 233 | // Don't append a separator if the path is empty (indicating the current |
michael@0 | 234 | // directory) or if the path component is empty (indicating nothing to |
michael@0 | 235 | // append). |
michael@0 | 236 | if (component.length() > 0 && new_path.path_.length() > 0) { |
michael@0 | 237 | |
michael@0 | 238 | // Don't append a separator if the path still ends with a trailing |
michael@0 | 239 | // separator after stripping (indicating the root directory). |
michael@0 | 240 | if (!IsSeparator(new_path.path_[new_path.path_.length() - 1])) { |
michael@0 | 241 | |
michael@0 | 242 | // Don't append a separator if the path is just a drive letter. |
michael@0 | 243 | if (FindDriveLetter(new_path.path_) + 1 != new_path.path_.length()) { |
michael@0 | 244 | new_path.path_.append(1, kSeparators[0]); |
michael@0 | 245 | } |
michael@0 | 246 | } |
michael@0 | 247 | } |
michael@0 | 248 | |
michael@0 | 249 | new_path.path_.append(component); |
michael@0 | 250 | return new_path; |
michael@0 | 251 | } |
michael@0 | 252 | |
michael@0 | 253 | FilePath FilePath::Append(const FilePath& component) const { |
michael@0 | 254 | return Append(component.value()); |
michael@0 | 255 | } |
michael@0 | 256 | |
michael@0 | 257 | FilePath FilePath::AppendASCII(const std::string& component) const { |
michael@0 | 258 | DCHECK(IsStringASCII(component)); |
michael@0 | 259 | #if defined(OS_WIN) |
michael@0 | 260 | return Append(ASCIIToWide(component)); |
michael@0 | 261 | #elif defined(OS_POSIX) |
michael@0 | 262 | return Append(component); |
michael@0 | 263 | #endif |
michael@0 | 264 | } |
michael@0 | 265 | |
michael@0 | 266 | bool FilePath::IsAbsolute() const { |
michael@0 | 267 | return IsPathAbsolute(path_); |
michael@0 | 268 | } |
michael@0 | 269 | |
michael@0 | 270 | #if defined(OS_POSIX) |
michael@0 | 271 | // See file_path.h for a discussion of the encoding of paths on POSIX |
michael@0 | 272 | // platforms. These *Hack() functions are not quite correct, but they're |
michael@0 | 273 | // only temporary while we fix the remainder of the code. |
michael@0 | 274 | // Remember to remove the #includes at the top when you remove these. |
michael@0 | 275 | |
michael@0 | 276 | // static |
michael@0 | 277 | FilePath FilePath::FromWStringHack(const std::wstring& wstring) { |
michael@0 | 278 | return FilePath(base::SysWideToNativeMB(wstring)); |
michael@0 | 279 | } |
michael@0 | 280 | std::wstring FilePath::ToWStringHack() const { |
michael@0 | 281 | return base::SysNativeMBToWide(path_); |
michael@0 | 282 | } |
michael@0 | 283 | #elif defined(OS_WIN) |
michael@0 | 284 | // static |
michael@0 | 285 | FilePath FilePath::FromWStringHack(const std::wstring& wstring) { |
michael@0 | 286 | return FilePath(wstring); |
michael@0 | 287 | } |
michael@0 | 288 | std::wstring FilePath::ToWStringHack() const { |
michael@0 | 289 | return path_; |
michael@0 | 290 | } |
michael@0 | 291 | #endif |
michael@0 | 292 | |
michael@0 | 293 | void FilePath::OpenInputStream(std::ifstream& stream) const { |
michael@0 | 294 | stream.open( |
michael@0 | 295 | #ifndef __MINGW32__ |
michael@0 | 296 | path_.c_str(), |
michael@0 | 297 | #else |
michael@0 | 298 | base::SysWideToNativeMB(path_).c_str(), |
michael@0 | 299 | #endif |
michael@0 | 300 | std::ios::in | std::ios::binary); |
michael@0 | 301 | } |
michael@0 | 302 | |
michael@0 | 303 | FilePath FilePath::StripTrailingSeparators() const { |
michael@0 | 304 | FilePath new_path(path_); |
michael@0 | 305 | new_path.StripTrailingSeparatorsInternal(); |
michael@0 | 306 | |
michael@0 | 307 | return new_path; |
michael@0 | 308 | } |
michael@0 | 309 | |
michael@0 | 310 | void FilePath::StripTrailingSeparatorsInternal() { |
michael@0 | 311 | // If there is no drive letter, start will be 1, which will prevent stripping |
michael@0 | 312 | // the leading separator if there is only one separator. If there is a drive |
michael@0 | 313 | // letter, start will be set appropriately to prevent stripping the first |
michael@0 | 314 | // separator following the drive letter, if a separator immediately follows |
michael@0 | 315 | // the drive letter. |
michael@0 | 316 | StringType::size_type start = FindDriveLetter(path_) + 2; |
michael@0 | 317 | |
michael@0 | 318 | StringType::size_type last_stripped = StringType::npos; |
michael@0 | 319 | for (StringType::size_type pos = path_.length(); |
michael@0 | 320 | pos > start && IsSeparator(path_[pos - 1]); |
michael@0 | 321 | --pos) { |
michael@0 | 322 | // If the string only has two separators and they're at the beginning, |
michael@0 | 323 | // don't strip them, unless the string began with more than two separators. |
michael@0 | 324 | if (pos != start + 1 || last_stripped == start + 2 || |
michael@0 | 325 | !IsSeparator(path_[start - 1])) { |
michael@0 | 326 | path_.resize(pos - 1); |
michael@0 | 327 | last_stripped = pos; |
michael@0 | 328 | } |
michael@0 | 329 | } |
michael@0 | 330 | } |