ipc/chromium/src/base/file_path.cc

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

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 }

mercurial