LCOV - code coverage report
Current view: top level - ipc/chromium/src/base - file_path.cc (source / functions) Hit Total Coverage
Test: output.info Lines: 25 128 19.5 %
Date: 2017-07-14 16:53:18 Functions: 5 18 27.8 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13