| Index: base/file_path.cc | 
| diff --git a/base/file_path.cc b/base/file_path.cc | 
| index 1787a697eba86709b7e78603def15368effa38ff..dd80eab916aa159b3400b928966ddbf94339a783 100644 | 
| --- a/base/file_path.cc | 
| +++ b/base/file_path.cc | 
| @@ -36,16 +36,18 @@ const FilePath::CharType FilePath::kParentDirectory[] = FILE_PATH_LITERAL(".."); | 
|  | 
| const FilePath::CharType FilePath::kExtensionSeparator = FILE_PATH_LITERAL('.'); | 
|  | 
| +typedef FilePath::StringType StringType; | 
|  | 
| namespace { | 
|  | 
| +const char* kCommonDoubleExtensions[] = { "gz", "z", "bz2" }; | 
| + | 
| // If this FilePath contains a drive letter specification, returns the | 
| // position of the last character of the drive letter specification, | 
| // otherwise returns npos.  This can only be true on Windows, when a pathname | 
| // begins with a letter followed by a colon.  On other platforms, this always | 
| // returns npos. | 
| -FilePath::StringType::size_type FindDriveLetter( | 
| -    const FilePath::StringType& path) { | 
| +StringType::size_type FindDriveLetter(const StringType& path) { | 
| #if defined(FILE_PATH_USES_DRIVE_LETTERS) | 
| // This is dependent on an ASCII-based character set, but that's a | 
| // reasonable assumption.  iswalpha can be too inclusive here. | 
| @@ -55,35 +57,33 @@ FilePath::StringType::size_type FindDriveLetter( | 
| return 1; | 
| } | 
| #endif  // FILE_PATH_USES_DRIVE_LETTERS | 
| -  return FilePath::StringType::npos; | 
| +  return StringType::npos; | 
| } | 
|  | 
| - | 
| #if defined(FILE_PATH_USES_DRIVE_LETTERS) | 
| -bool EqualDriveLetterCaseInsensitive(const FilePath::StringType a, | 
| -                                     const FilePath::StringType b) { | 
| +bool EqualDriveLetterCaseInsensitive(const StringType a, | 
| +                                     const StringType b) { | 
| size_t a_letter_pos = FindDriveLetter(a); | 
| size_t b_letter_pos = FindDriveLetter(b); | 
|  | 
| -  if ((a_letter_pos == FilePath::StringType::npos) || | 
| -      (b_letter_pos == FilePath::StringType::npos)) | 
| +  if (a_letter_pos == StringType::npos || b_letter_pos == StringType::npos) | 
| return a == b; | 
|  | 
| -  FilePath::StringType a_letter(a.substr(0, a_letter_pos + 1)); | 
| -  FilePath::StringType b_letter(b.substr(0, b_letter_pos + 1)); | 
| +  StringType a_letter(a.substr(0, a_letter_pos + 1)); | 
| +  StringType b_letter(b.substr(0, b_letter_pos + 1)); | 
| if (!StartsWith(a_letter, b_letter, false)) | 
| return false; | 
|  | 
| -  FilePath::StringType a_rest(a.substr(a_letter_pos + 1)); | 
| -  FilePath::StringType b_rest(b.substr(b_letter_pos + 1)); | 
| +  StringType a_rest(a.substr(a_letter_pos + 1)); | 
| +  StringType b_rest(b.substr(b_letter_pos + 1)); | 
| return a_rest == b_rest; | 
| } | 
| #endif  // defined(FILE_PATH_USES_DRIVE_LETTERS) | 
|  | 
| -bool IsPathAbsolute(const FilePath::StringType& path) { | 
| +bool IsPathAbsolute(const StringType& path) { | 
| #if defined(FILE_PATH_USES_DRIVE_LETTERS) | 
| -  FilePath::StringType::size_type letter = FindDriveLetter(path); | 
| -  if (letter != FilePath::StringType::npos) { | 
| +  StringType::size_type letter = FindDriveLetter(path); | 
| +  if (letter != StringType::npos) { | 
| // Look for a separator right after the drive specification. | 
| return path.length() > letter + 1 && | 
| FilePath::IsSeparator(path[letter + 1]); | 
| @@ -97,8 +97,8 @@ bool IsPathAbsolute(const FilePath::StringType& path) { | 
| #endif  // FILE_PATH_USES_DRIVE_LETTERS | 
| } | 
|  | 
| -bool AreAllSeparators(const FilePath::StringType& input) { | 
| -  for (FilePath::StringType::const_iterator it = input.begin(); | 
| +bool AreAllSeparators(const StringType& input) { | 
| +  for (StringType::const_iterator it = input.begin(); | 
| it != input.end(); ++it) { | 
| if (!FilePath::IsSeparator(*it)) | 
| return false; | 
| @@ -107,6 +107,54 @@ bool AreAllSeparators(const FilePath::StringType& input) { | 
| return true; | 
| } | 
|  | 
| +// Find the position of the '.' that separates the extension from the rest | 
| +// of the file name. The position is relative to BaseName(), not value(). | 
| +// This allows a second extension component of up to 4 characters when the | 
| +// rightmost extension component is a common double extension (gz, bz2, Z). | 
| +// For example, foo.tar.gz or foo.tar.Z would have extension components of | 
| +// '.tar.gz' and '.tar.Z' respectively. Returns npos if it can't find an | 
| +// extension. | 
| +StringType::size_type ExtensionSeparatorPosition(const StringType& path) { | 
| +  // Special case "." and ".." | 
| +  if (path == FilePath::kCurrentDirectory || path == FilePath::kParentDirectory) | 
| +    return StringType::npos; | 
| + | 
| +  const StringType::size_type last_dot = | 
| +      path.rfind(FilePath::kExtensionSeparator); | 
| + | 
| +  // No extension, or the extension is the whole filename. | 
| +  if (last_dot == StringType::npos || last_dot == 0U) | 
| +    return last_dot; | 
| + | 
| +  // Special case .<extension1>.<extension2>, but only if the final extension | 
| +  // is one of a few common double extensions. | 
| +  StringType extension(path, last_dot + 1); | 
| +  bool is_common_double_extension = false; | 
| +  for (size_t i = 0; i < arraysize(kCommonDoubleExtensions); ++i) { | 
| +    if (LowerCaseEqualsASCII(extension, kCommonDoubleExtensions[i])) | 
| +      is_common_double_extension = true; | 
| +  } | 
| +  if (!is_common_double_extension) | 
| +    return last_dot; | 
| + | 
| +  // Check that <extension1> is 1-4 characters, otherwise fall back to | 
| +  // <extension2>. | 
| +  const StringType::size_type penultimate_dot = | 
| +      path.rfind(FilePath::kExtensionSeparator, last_dot - 1); | 
| +  const StringType::size_type last_separator = | 
| +      path.find_last_of(FilePath::kSeparators, last_dot - 1, | 
| +                        arraysize(FilePath::kSeparators) - 1); | 
| +  if (penultimate_dot != StringType::npos && | 
| +      (last_separator == StringType::npos || | 
| +      penultimate_dot > last_separator) && | 
| +      last_dot - penultimate_dot <= 5U && | 
| +      last_dot - penultimate_dot > 1U) { | 
| +    return penultimate_dot; | 
| +  } | 
| + | 
| +  return last_dot; | 
| +} | 
| + | 
| }  // namespace | 
|  | 
| FilePath::FilePath() { | 
| @@ -136,8 +184,7 @@ bool FilePath::IsSeparator(CharType character) { | 
| return false; | 
| } | 
|  | 
| -void FilePath::GetComponents(std::vector<FilePath::StringType>* components) | 
| -    const { | 
| +void FilePath::GetComponents(std::vector<StringType>* components) const { | 
| DCHECK(components); | 
| if (!components) | 
| return; | 
| @@ -145,7 +192,7 @@ void FilePath::GetComponents(std::vector<FilePath::StringType>* components) | 
| if (value().empty()) | 
| return; | 
|  | 
| -  std::vector<FilePath::StringType> ret_val; | 
| +  std::vector<StringType> ret_val; | 
| FilePath current = *this; | 
| FilePath base; | 
|  | 
| @@ -165,12 +212,11 @@ void FilePath::GetComponents(std::vector<FilePath::StringType>* components) | 
| // Capture drive letter, if any. | 
| FilePath dir = current.DirName(); | 
| StringType::size_type letter = FindDriveLetter(dir.value()); | 
| -  if (letter != FilePath::StringType::npos) { | 
| -    ret_val.push_back(FilePath::StringType(dir.value(), 0, letter + 1)); | 
| +  if (letter != StringType::npos) { | 
| +    ret_val.push_back(StringType(dir.value(), 0, letter + 1)); | 
| } | 
|  | 
| -  *components = std::vector<FilePath::StringType>(ret_val.rbegin(), | 
| -                                                  ret_val.rend()); | 
| +  *components = std::vector<StringType>(ret_val.rbegin(), ret_val.rend()); | 
| } | 
|  | 
| bool FilePath::operator==(const FilePath& that) const { | 
| @@ -195,8 +241,8 @@ bool FilePath::IsParent(const FilePath& child) const { | 
|  | 
| bool FilePath::AppendRelativePath(const FilePath& child, | 
| FilePath* path) const { | 
| -  std::vector<FilePath::StringType> parent_components; | 
| -  std::vector<FilePath::StringType> child_components; | 
| +  std::vector<StringType> parent_components; | 
| +  std::vector<StringType> child_components; | 
| GetComponents(&parent_components); | 
| child.GetComponents(&child_components); | 
|  | 
| @@ -205,17 +251,17 @@ bool FilePath::AppendRelativePath(const FilePath& child, | 
| if (parent_components.size() == 0) | 
| return false; | 
|  | 
| -  std::vector<FilePath::StringType>::const_iterator parent_comp = | 
| +  std::vector<StringType>::const_iterator parent_comp = | 
| parent_components.begin(); | 
| -  std::vector<FilePath::StringType>::const_iterator child_comp = | 
| +  std::vector<StringType>::const_iterator child_comp = | 
| child_components.begin(); | 
|  | 
| #if defined(FILE_PATH_USES_DRIVE_LETTERS) | 
| // Windows can access case sensitive filesystems, so component | 
| // comparisions must be case sensitive, but drive letters are | 
| // never case sensitive. | 
| -  if ((FindDriveLetter(*parent_comp) != FilePath::StringType::npos) && | 
| -      (FindDriveLetter(*child_comp) != FilePath::StringType::npos)) { | 
| +  if ((FindDriveLetter(*parent_comp) != StringType::npos) && | 
| +      (FindDriveLetter(*child_comp) != StringType::npos)) { | 
| if (!StartsWith(*parent_comp, *child_comp, false)) | 
| return false; | 
| ++parent_comp; | 
| @@ -301,30 +347,24 @@ FilePath FilePath::BaseName() const { | 
| return new_path; | 
| } | 
|  | 
| -FilePath::StringType FilePath::Extension() const { | 
| -  // BaseName() calls StripTrailingSeparators, so cases like /foo.baz/// work. | 
| -  StringType base = BaseName().value(); | 
| - | 
| -  // Special case "." and ".." | 
| -  if (base == kCurrentDirectory || base == kParentDirectory) | 
| +StringType FilePath::Extension() const { | 
| +  FilePath base(BaseName()); | 
| +  const StringType::size_type dot = ExtensionSeparatorPosition(base.path_); | 
| +  if (dot == StringType::npos) | 
| return StringType(); | 
|  | 
| -  const StringType::size_type last_dot = base.rfind(kExtensionSeparator); | 
| -  if (last_dot == StringType::npos) | 
| -    return StringType(); | 
| -  return StringType(base, last_dot); | 
| +  return base.path_.substr(dot, StringType::npos); | 
| } | 
|  | 
| FilePath FilePath::RemoveExtension() const { | 
| -  StringType ext = Extension(); | 
| -  // It's important to check Extension() since that verifies that the | 
| -  // kExtensionSeparator actually appeared in the last path component. | 
| -  if (ext.empty()) | 
| -    return FilePath(path_); | 
| -  // Since Extension() verified that the extension is in fact in the last path | 
| -  // component, this substr will effectively strip trailing separators. | 
| -  const StringType::size_type last_dot = path_.rfind(kExtensionSeparator); | 
| -  return FilePath(path_.substr(0, last_dot)); | 
| +  if (Extension().empty()) | 
| +    return *this; | 
| + | 
| +  const StringType::size_type dot = ExtensionSeparatorPosition(path_); | 
| +  if (dot == StringType::npos) | 
| +    return *this; | 
| + | 
| +  return FilePath(path_.substr(0, dot)); | 
| } | 
|  | 
| FilePath FilePath::InsertBeforeExtension(const StringType& suffix) const { | 
| @@ -390,7 +430,7 @@ FilePath FilePath::ReplaceExtension(const StringType& extension) const { | 
| bool FilePath::MatchesExtension(const StringType& extension) const { | 
| DCHECK(extension.empty() || extension[0] == kExtensionSeparator); | 
|  | 
| -  FilePath::StringType current_extension = Extension(); | 
| +  StringType current_extension = Extension(); | 
|  | 
| if (current_extension.length() != extension.length()) | 
| return false; | 
| @@ -950,7 +990,7 @@ int FilePath::HFSFastUnicodeCompare(const StringType& string1, | 
| } | 
| } | 
|  | 
| -FilePath::StringType FilePath::GetHFSDecomposedForm(const StringType& string) { | 
| +StringType FilePath::GetHFSDecomposedForm(const StringType& string) { | 
| scoped_cftyperef<CFStringRef> cfstring( | 
| CFStringCreateWithBytesNoCopy( | 
| NULL, | 
| @@ -1071,7 +1111,7 @@ FilePath FilePath::StripTrailingSeparators() const { | 
|  | 
| // static. | 
| void FilePath::WriteStringTypeToPickle(Pickle* pickle, | 
| -                                       const FilePath::StringType& path) { | 
| +                                       const StringType& path) { | 
| #if defined(WCHAR_T_IS_UTF16) | 
| pickle->WriteWString(path); | 
| #elif defined(WCHAR_T_IS_UTF32) | 
| @@ -1083,7 +1123,7 @@ void FilePath::WriteStringTypeToPickle(Pickle* pickle, | 
|  | 
| // static. | 
| bool FilePath::ReadStringTypeFromPickle(Pickle* pickle, void** iter, | 
| -                                        FilePath::StringType* path) { | 
| +                                        StringType* path) { | 
| #if defined(WCHAR_T_IS_UTF16) | 
| if (!pickle->ReadWString(iter, path)) | 
| return false; | 
| @@ -1129,12 +1169,12 @@ void FilePath::StripTrailingSeparatorsInternal() { | 
| } | 
|  | 
| bool FilePath::ReferencesParent() const { | 
| -  std::vector<FilePath::StringType> components; | 
| +  std::vector<StringType> components; | 
| GetComponents(&components); | 
|  | 
| -  std::vector<FilePath::StringType>::const_iterator it = components.begin(); | 
| +  std::vector<StringType>::const_iterator it = components.begin(); | 
| for (; it != components.end(); ++it) { | 
| -    const FilePath::StringType& component = *it; | 
| +    const StringType& component = *it; | 
| if (component == kParentDirectory) | 
| return true; | 
| } | 
|  |