OLD | NEW |
1 // Copyright (c) 2008 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2008 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "base/file_path.h" | 5 #include "base/file_path.h" |
| 6 #include "base/logging.h" |
6 | 7 |
7 // These includes are just for the *Hack functions, and should be removed | 8 // These includes are just for the *Hack functions, and should be removed |
8 // when those functions are removed. | 9 // when those functions are removed. |
9 #include "base/string_piece.h" | 10 #include "base/string_piece.h" |
10 #include "base/sys_string_conversions.h" | 11 #include "base/sys_string_conversions.h" |
11 | 12 |
12 #if defined(FILE_PATH_USES_WIN_SEPARATORS) | 13 #if defined(FILE_PATH_USES_WIN_SEPARATORS) |
13 const FilePath::CharType FilePath::kSeparators[] = FILE_PATH_LITERAL("\\/"); | 14 const FilePath::CharType FilePath::kSeparators[] = FILE_PATH_LITERAL("\\/"); |
14 #else // FILE_PATH_USES_WIN_SEPARATORS | 15 #else // FILE_PATH_USES_WIN_SEPARATORS |
15 const FilePath::CharType FilePath::kSeparators[] = FILE_PATH_LITERAL("/"); | 16 const FilePath::CharType FilePath::kSeparators[] = FILE_PATH_LITERAL("/"); |
16 #endif // FILE_PATH_USES_WIN_SEPARATORS | 17 #endif // FILE_PATH_USES_WIN_SEPARATORS |
17 | 18 |
18 const FilePath::CharType FilePath::kCurrentDirectory[] = FILE_PATH_LITERAL("."); | 19 const FilePath::CharType FilePath::kCurrentDirectory[] = FILE_PATH_LITERAL("."); |
19 const FilePath::CharType FilePath::kParentDirectory[] = FILE_PATH_LITERAL(".."); | 20 const FilePath::CharType FilePath::kParentDirectory[] = FILE_PATH_LITERAL(".."); |
20 | 21 |
| 22 namespace { |
| 23 |
| 24 // If this FilePath contains a drive letter specification, returns the |
| 25 // position of the last character of the drive letter specification, |
| 26 // otherwise returns npos. This can only be true on Windows, when a pathname |
| 27 // begins with a letter followed by a colon. On other platforms, this always |
| 28 // returns npos. |
| 29 FilePath::StringType::size_type FindDriveLetter( |
| 30 const FilePath::StringType& path) { |
| 31 #if defined(FILE_PATH_USES_DRIVE_LETTERS) |
| 32 // This is dependent on an ASCII-based character set, but that's a |
| 33 // reasonable assumption. iswalpha can be too inclusive here. |
| 34 if (path.length() >= 2 && path[1] == L':' && |
| 35 ((path[0] >= L'A' && path[0] <= L'Z') || |
| 36 (path[0] >= L'a' && path[0] <= L'z'))) { |
| 37 return 1; |
| 38 } |
| 39 return FilePath::StringType::npos; |
| 40 #else // FILE_PATH_USES_DRIVE_LETTERS |
| 41 return FilePath::StringType::npos; |
| 42 #endif // FILE_PATH_USES_DRIVE_LETTERS |
| 43 } |
| 44 |
| 45 bool IsPathAbsolute(const FilePath::StringType& path) { |
| 46 #if defined(FILE_PATH_USES_DRIVE_LETTERS) |
| 47 FilePath::StringType::size_type letter = FindDriveLetter(path); |
| 48 if (letter != FilePath::StringType::npos) { |
| 49 // Look for a separator right after the drive specification. |
| 50 return path.length() > letter + 1 && |
| 51 FilePath::IsSeparator(path[letter + 1]); |
| 52 } |
| 53 // Look for a pair of leading separators. |
| 54 return path.length() > 1 && |
| 55 FilePath::IsSeparator(path[0]) && FilePath::IsSeparator(path[1]); |
| 56 #else // FILE_PATH_USES_DRIVE_LETTERS |
| 57 // Look for a separator in the first position. |
| 58 return path.length() > 0 && FilePath::IsSeparator(path[0]); |
| 59 #endif // FILE_PATH_USES_DRIVE_LETTERS |
| 60 } |
| 61 |
| 62 } // namespace |
| 63 |
21 bool FilePath::IsSeparator(CharType character) { | 64 bool FilePath::IsSeparator(CharType character) { |
22 for (size_t i = 0; i < arraysize(kSeparators) - 1; ++i) { | 65 for (size_t i = 0; i < arraysize(kSeparators) - 1; ++i) { |
23 if (character == kSeparators[i]) { | 66 if (character == kSeparators[i]) { |
24 return true; | 67 return true; |
25 } | 68 } |
26 } | 69 } |
27 | 70 |
28 return false; | 71 return false; |
29 } | 72 } |
30 | 73 |
31 // libgen's dirname and basename aren't guaranteed to be thread-safe and aren't | 74 // libgen's dirname and basename aren't guaranteed to be thread-safe and aren't |
32 // guaranteed to not modify their input strings, and in fact are implmeneted | 75 // guaranteed to not modify their input strings, and in fact are implmeneted |
33 // differently in this regard on different platforms. Don't use them, but | 76 // differently in this regard on different platforms. Don't use them, but |
34 // adhere to their behavior. | 77 // adhere to their behavior. |
35 FilePath FilePath::DirName() const { | 78 FilePath FilePath::DirName() const { |
36 FilePath new_path(path_); | 79 FilePath new_path(path_); |
37 new_path.StripTrailingSeparators(); | 80 new_path.StripTrailingSeparators(); |
38 | 81 |
39 // The drive letter, if any, always needs to remain in the output. If there | 82 // The drive letter, if any, always needs to remain in the output. If there |
40 // is no drive letter, as will always be the case on platforms which do not | 83 // is no drive letter, as will always be the case on platforms which do not |
41 // support drive letters, letter will be npos, or -1, so the comparisons and | 84 // support drive letters, letter will be npos, or -1, so the comparisons and |
42 // resizes below using letter will still be valid. | 85 // resizes below using letter will still be valid. |
43 StringType::size_type letter = new_path.FindDriveLetter(); | 86 StringType::size_type letter = FindDriveLetter(new_path.path_); |
44 | 87 |
45 StringType::size_type last_separator = | 88 StringType::size_type last_separator = |
46 new_path.path_.find_last_of(kSeparators, StringType::npos, | 89 new_path.path_.find_last_of(kSeparators, StringType::npos, |
47 arraysize(kSeparators) - 1); | 90 arraysize(kSeparators) - 1); |
48 if (last_separator == StringType::npos) { | 91 if (last_separator == StringType::npos) { |
49 // path_ is in the current directory. | 92 // path_ is in the current directory. |
50 new_path.path_.resize(letter + 1); | 93 new_path.path_.resize(letter + 1); |
51 } else if (last_separator == letter + 1) { | 94 } else if (last_separator == letter + 1) { |
52 // path_ is in the root directory. | 95 // path_ is in the root directory. |
53 new_path.path_.resize(letter + 2); | 96 new_path.path_.resize(letter + 2); |
(...skipping 12 matching lines...) Expand all Loading... |
66 new_path.path_ = kCurrentDirectory; | 109 new_path.path_ = kCurrentDirectory; |
67 | 110 |
68 return new_path; | 111 return new_path; |
69 } | 112 } |
70 | 113 |
71 FilePath FilePath::BaseName() const { | 114 FilePath FilePath::BaseName() const { |
72 FilePath new_path(path_); | 115 FilePath new_path(path_); |
73 new_path.StripTrailingSeparators(); | 116 new_path.StripTrailingSeparators(); |
74 | 117 |
75 // The drive letter, if any, is always stripped. | 118 // The drive letter, if any, is always stripped. |
76 StringType::size_type letter = new_path.FindDriveLetter(); | 119 StringType::size_type letter = FindDriveLetter(new_path.path_); |
77 if (letter != StringType::npos) { | 120 if (letter != StringType::npos) { |
78 new_path.path_.erase(0, letter + 1); | 121 new_path.path_.erase(0, letter + 1); |
79 } | 122 } |
80 | 123 |
81 // Keep everything after the final separator, but if the pathname is only | 124 // Keep everything after the final separator, but if the pathname is only |
82 // one character and it's a separator, leave it alone. | 125 // one character and it's a separator, leave it alone. |
83 StringType::size_type last_separator = | 126 StringType::size_type last_separator = |
84 new_path.path_.find_last_of(kSeparators, StringType::npos, | 127 new_path.path_.find_last_of(kSeparators, StringType::npos, |
85 arraysize(kSeparators) - 1); | 128 arraysize(kSeparators) - 1); |
86 if (last_separator != StringType::npos && | 129 if (last_separator != StringType::npos && |
87 last_separator < new_path.path_.length() - 1) { | 130 last_separator < new_path.path_.length() - 1) { |
88 new_path.path_.erase(0, last_separator + 1); | 131 new_path.path_.erase(0, last_separator + 1); |
89 } | 132 } |
90 | 133 |
91 return new_path; | 134 return new_path; |
92 } | 135 } |
93 | 136 |
94 FilePath FilePath::Append(const FilePath::StringType& component) const { | 137 FilePath FilePath::Append(const FilePath::StringType& component) const { |
| 138 DCHECK(!IsPathAbsolute(component)); |
95 if (path_.compare(kCurrentDirectory) == 0) { | 139 if (path_.compare(kCurrentDirectory) == 0) { |
96 // Append normally doesn't do any normalization, but as a special case, | 140 // Append normally doesn't do any normalization, but as a special case, |
97 // when appending to kCurrentDirectory, just return a new path for the | 141 // when appending to kCurrentDirectory, just return a new path for the |
98 // component argument. Appending component to kCurrentDirectory would | 142 // component argument. Appending component to kCurrentDirectory would |
99 // serve no purpose other than needlessly lengthening the path, and | 143 // serve no purpose other than needlessly lengthening the path, and |
100 // it's likely in practice to wind up with FilePath objects containing | 144 // it's likely in practice to wind up with FilePath objects containing |
101 // only kCurrentDirectory when calling DirName on a single relative path | 145 // only kCurrentDirectory when calling DirName on a single relative path |
102 // component. | 146 // component. |
103 return FilePath(component); | 147 return FilePath(component); |
104 } | 148 } |
105 | 149 |
106 FilePath new_path(path_); | 150 FilePath new_path(path_); |
107 new_path.StripTrailingSeparators(); | 151 new_path.StripTrailingSeparators(); |
108 | 152 |
109 // Don't append a separator if the path is empty (indicating the current | 153 // Don't append a separator if the path is empty (indicating the current |
110 // directory) or if the path component is empty (indicating nothing to | 154 // directory) or if the path component is empty (indicating nothing to |
111 // append). | 155 // append). |
112 if (component.length() > 0 && new_path.path_.length() > 0) { | 156 if (component.length() > 0 && new_path.path_.length() > 0) { |
113 | 157 |
114 // Don't append a separator if the path still ends with a trailing | 158 // Don't append a separator if the path still ends with a trailing |
115 // separator after stripping (indicating the root directory). | 159 // separator after stripping (indicating the root directory). |
116 if (!IsSeparator(new_path.path_[new_path.path_.length() - 1])) { | 160 if (!IsSeparator(new_path.path_[new_path.path_.length() - 1])) { |
117 | 161 |
118 // Don't append a separator if the path is just a drive letter. | 162 // Don't append a separator if the path is just a drive letter. |
119 if (new_path.FindDriveLetter() + 1 != new_path.path_.length()) { | 163 if (FindDriveLetter(new_path.path_) + 1 != new_path.path_.length()) { |
120 new_path.path_.append(1, kSeparators[0]); | 164 new_path.path_.append(1, kSeparators[0]); |
121 } | 165 } |
122 } | 166 } |
123 } | 167 } |
124 | 168 |
125 new_path.path_.append(component); | 169 new_path.path_.append(component); |
126 return new_path; | 170 return new_path; |
127 } | 171 } |
128 | 172 |
129 FilePath::StringType::size_type FilePath::FindDriveLetter() const { | 173 FilePath FilePath::Append(const FilePath& component) const { |
130 #if defined(FILE_PATH_USES_DRIVE_LETTERS) | 174 return Append(component.value()); |
131 // This is dependent on an ASCII-based character set, but that's a | |
132 // reasonable assumption. iswalpha can be too inclusive here. | |
133 if (path_.length() >= 2 && path_[1] == L':' && | |
134 ((path_[0] >= L'A' && path_[0] <= L'Z') || | |
135 (path_[0] >= L'a' && path_[0] <= L'z'))) { | |
136 return 1; | |
137 } | |
138 return StringType::npos; | |
139 #else // FILE_PATH_USES_DRIVE_LETTERS | |
140 return StringType::npos; | |
141 #endif // FILE_PATH_USES_DRIVE_LETTERS | |
142 } | 175 } |
143 | 176 |
144 bool FilePath::IsAbsolute() const { | 177 bool FilePath::IsAbsolute() const { |
145 #if defined(FILE_PATH_USES_DRIVE_LETTERS) | 178 return IsPathAbsolute(path_); |
146 StringType::size_type letter = FindDriveLetter(); | |
147 if (letter != StringType::npos) { | |
148 // Look for a separator right after the drive specification. | |
149 return path_.length() > letter + 1 && IsSeparator(path_[letter + 1]); | |
150 } | |
151 // Look for a pair of leading separators. | |
152 return path_.length() > 1 && IsSeparator(path_[0]) && IsSeparator(path_[1]); | |
153 #else // FILE_PATH_USES_DRIVE_LETTERS | |
154 // Look for a separator in the first position. | |
155 return path_.length() > 0 && IsSeparator(path_[0]); | |
156 #endif // FILE_PATH_USES_DRIVE_LETTERS | |
157 } | 179 } |
158 | 180 |
159 #if defined(OS_POSIX) | 181 #if defined(OS_POSIX) |
160 // See file_path.h for a discussion of the encoding of paths on POSIX | 182 // See file_path.h for a discussion of the encoding of paths on POSIX |
161 // platforms. These *Hack() functions are not quite correct, but they're | 183 // platforms. These *Hack() functions are not quite correct, but they're |
162 // only temporary while we fix the remainder of the code. | 184 // only temporary while we fix the remainder of the code. |
163 // Remember to remove the #includes at the top when you remove these. | 185 // Remember to remove the #includes at the top when you remove these. |
164 | 186 |
165 // static | 187 // static |
166 FilePath FilePath::FromWStringHack(const std::wstring& wstring) { | 188 FilePath FilePath::FromWStringHack(const std::wstring& wstring) { |
(...skipping 11 matching lines...) Expand all Loading... |
178 return path_; | 200 return path_; |
179 } | 201 } |
180 #endif | 202 #endif |
181 | 203 |
182 void FilePath::StripTrailingSeparators() { | 204 void FilePath::StripTrailingSeparators() { |
183 // If there is no drive letter, start will be 1, which will prevent stripping | 205 // If there is no drive letter, start will be 1, which will prevent stripping |
184 // the leading separator if there is only one separator. If there is a drive | 206 // the leading separator if there is only one separator. If there is a drive |
185 // letter, start will be set appropriately to prevent stripping the first | 207 // letter, start will be set appropriately to prevent stripping the first |
186 // separator following the drive letter, if a separator immediately follows | 208 // separator following the drive letter, if a separator immediately follows |
187 // the drive letter. | 209 // the drive letter. |
188 StringType::size_type start = FindDriveLetter() + 2; | 210 StringType::size_type start = FindDriveLetter(path_) + 2; |
189 | 211 |
190 StringType::size_type last_stripped = StringType::npos; | 212 StringType::size_type last_stripped = StringType::npos; |
191 for (StringType::size_type pos = path_.length(); | 213 for (StringType::size_type pos = path_.length(); |
192 pos > start && IsSeparator(path_[pos - 1]); | 214 pos > start && IsSeparator(path_[pos - 1]); |
193 --pos) { | 215 --pos) { |
194 // If the string only has two separators and they're at the beginning, | 216 // If the string only has two separators and they're at the beginning, |
195 // don't strip them, unless the string began with more than two separators. | 217 // don't strip them, unless the string began with more than two separators. |
196 if (pos != start + 1 || last_stripped == start + 2 || | 218 if (pos != start + 1 || last_stripped == start + 2 || |
197 !IsSeparator(path_[start - 1])) { | 219 !IsSeparator(path_[start - 1])) { |
198 path_.resize(pos - 1); | 220 path_.resize(pos - 1); |
199 last_stripped = pos; | 221 last_stripped = pos; |
200 } | 222 } |
201 } | 223 } |
202 } | 224 } |
OLD | NEW |