1/*
2 *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
3 *
4 *  Use of this source code is governed by a BSD-style license
5 *  that can be found in the LICENSE file in the root of the source
6 *  tree. An additional intellectual property rights grant can be found
7 *  in the file PATENTS.  All contributing project authors may
8 *  be found in the AUTHORS file in the root of the source tree.
9 */
10
11#if defined(WEBRTC_WIN)
12#include "webrtc/base/win32.h"
13#include <shellapi.h>
14#include <shlobj.h>
15#include <tchar.h>
16#endif  // WEBRTC_WIN
17
18#include "webrtc/base/common.h"
19#include "webrtc/base/fileutils.h"
20#include "webrtc/base/logging.h"
21#include "webrtc/base/pathutils.h"
22#include "webrtc/base/stringutils.h"
23#include "webrtc/base/urlencode.h"
24
25namespace rtc {
26
27static const char EMPTY_STR[] = "";
28
29// EXT_DELIM separates a file basename from extension
30const char EXT_DELIM = '.';
31
32// FOLDER_DELIMS separate folder segments and the filename
33const char* const FOLDER_DELIMS = "/\\";
34
35// DEFAULT_FOLDER_DELIM is the preferred delimiter for this platform
36#if WEBRTC_WIN
37const char DEFAULT_FOLDER_DELIM = '\\';
38#else  // !WEBRTC_WIN
39const char DEFAULT_FOLDER_DELIM = '/';
40#endif  // !WEBRTC_WIN
41
42///////////////////////////////////////////////////////////////////////////////
43// Pathname - parsing of pathnames into components, and vice versa
44///////////////////////////////////////////////////////////////////////////////
45
46bool Pathname::IsFolderDelimiter(char ch) {
47  return (NULL != ::strchr(FOLDER_DELIMS, ch));
48}
49
50char Pathname::DefaultFolderDelimiter() {
51  return DEFAULT_FOLDER_DELIM;
52}
53
54Pathname::Pathname()
55    : folder_delimiter_(DEFAULT_FOLDER_DELIM) {
56}
57
58Pathname::Pathname(const std::string& pathname)
59    : folder_delimiter_(DEFAULT_FOLDER_DELIM) {
60  SetPathname(pathname);
61}
62
63Pathname::Pathname(const std::string& folder, const std::string& filename)
64    : folder_delimiter_(DEFAULT_FOLDER_DELIM) {
65  SetPathname(folder, filename);
66}
67
68void Pathname::SetFolderDelimiter(char delimiter) {
69  ASSERT(IsFolderDelimiter(delimiter));
70  folder_delimiter_ = delimiter;
71}
72
73void Pathname::Normalize() {
74  for (size_t i=0; i<folder_.length(); ++i) {
75    if (IsFolderDelimiter(folder_[i])) {
76      folder_[i] = folder_delimiter_;
77    }
78  }
79}
80
81void Pathname::clear() {
82  folder_.clear();
83  basename_.clear();
84  extension_.clear();
85}
86
87bool Pathname::empty() const {
88  return folder_.empty() && basename_.empty() && extension_.empty();
89}
90
91std::string Pathname::pathname() const {
92  std::string pathname(folder_);
93  pathname.append(basename_);
94  pathname.append(extension_);
95  if (pathname.empty()) {
96    // Instead of the empty pathname, return the current working directory.
97    pathname.push_back('.');
98    pathname.push_back(folder_delimiter_);
99  }
100  return pathname;
101}
102
103std::string Pathname::url() const {
104  std::string s = "file:///";
105  for (size_t i=0; i<folder_.length(); ++i) {
106    if (IsFolderDelimiter(folder_[i]))
107      s += '/';
108    else
109      s += folder_[i];
110  }
111  s += basename_;
112  s += extension_;
113  return UrlEncodeStringForOnlyUnsafeChars(s);
114}
115
116void Pathname::SetPathname(const std::string& pathname) {
117  std::string::size_type pos = pathname.find_last_of(FOLDER_DELIMS);
118  if (pos != std::string::npos) {
119    SetFolder(pathname.substr(0, pos + 1));
120    SetFilename(pathname.substr(pos + 1));
121  } else {
122    SetFolder(EMPTY_STR);
123    SetFilename(pathname);
124  }
125}
126
127void Pathname::SetPathname(const std::string& folder,
128                           const std::string& filename) {
129  SetFolder(folder);
130  SetFilename(filename);
131}
132
133void Pathname::AppendPathname(const std::string& pathname) {
134  std::string full_pathname(folder_);
135  full_pathname.append(pathname);
136  SetPathname(full_pathname);
137}
138
139std::string Pathname::folder() const {
140  return folder_;
141}
142
143std::string Pathname::folder_name() const {
144  std::string::size_type pos = std::string::npos;
145  if (folder_.size() >= 2) {
146    pos = folder_.find_last_of(FOLDER_DELIMS, folder_.length() - 2);
147  }
148  if (pos != std::string::npos) {
149    return folder_.substr(pos + 1);
150  } else {
151    return folder_;
152  }
153}
154
155std::string Pathname::parent_folder() const {
156  std::string::size_type pos = std::string::npos;
157  if (folder_.size() >= 2) {
158    pos = folder_.find_last_of(FOLDER_DELIMS, folder_.length() - 2);
159  }
160  if (pos != std::string::npos) {
161    return folder_.substr(0, pos + 1);
162  } else {
163    return EMPTY_STR;
164  }
165}
166
167void Pathname::SetFolder(const std::string& folder) {
168  folder_.assign(folder);
169  // Ensure folder ends in a path delimiter
170  if (!folder_.empty() && !IsFolderDelimiter(folder_[folder_.length()-1])) {
171    folder_.push_back(folder_delimiter_);
172  }
173}
174
175void Pathname::AppendFolder(const std::string& folder) {
176  folder_.append(folder);
177  // Ensure folder ends in a path delimiter
178  if (!folder_.empty() && !IsFolderDelimiter(folder_[folder_.length()-1])) {
179    folder_.push_back(folder_delimiter_);
180  }
181}
182
183std::string Pathname::basename() const {
184  return basename_;
185}
186
187bool Pathname::SetBasename(const std::string& basename) {
188  if(basename.find_first_of(FOLDER_DELIMS) != std::string::npos) {
189    return false;
190  }
191  basename_.assign(basename);
192  return true;
193}
194
195std::string Pathname::extension() const {
196  return extension_;
197}
198
199bool Pathname::SetExtension(const std::string& extension) {
200  if (extension.find_first_of(FOLDER_DELIMS) != std::string::npos ||
201    extension.find_first_of(EXT_DELIM, 1) != std::string::npos) {
202      return false;
203  }
204  extension_.assign(extension);
205  // Ensure extension begins with the extension delimiter
206  if (!extension_.empty() && (extension_[0] != EXT_DELIM)) {
207    extension_.insert(extension_.begin(), EXT_DELIM);
208  }
209  return true;
210}
211
212std::string Pathname::filename() const {
213  std::string filename(basename_);
214  filename.append(extension_);
215  return filename;
216}
217
218bool Pathname::SetFilename(const std::string& filename) {
219  std::string::size_type pos = filename.rfind(EXT_DELIM);
220  if ((pos == std::string::npos) || (pos == 0)) {
221    return SetExtension(EMPTY_STR) && SetBasename(filename);
222  } else {
223    return SetExtension(filename.substr(pos)) && SetBasename(filename.substr(0, pos));
224  }
225}
226
227#if defined(WEBRTC_WIN)
228bool Pathname::GetDrive(char* drive, uint32_t bytes) const {
229  return GetDrive(drive, bytes, folder_);
230}
231
232// static
233bool Pathname::GetDrive(char* drive,
234                        uint32_t bytes,
235                        const std::string& pathname) {
236  // need at lease 4 bytes to save c:
237  if (bytes < 4 || pathname.size() < 3) {
238    return false;
239  }
240
241  memcpy(drive, pathname.c_str(), 3);
242  drive[3] = 0;
243  // sanity checking
244  return (isalpha(drive[0]) &&
245          drive[1] == ':' &&
246          drive[2] == '\\');
247}
248#endif
249
250///////////////////////////////////////////////////////////////////////////////
251
252} // namespace rtc
253