1 /* 2 * libjingle 3 * Copyright 2004--2005, Google Inc. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, 9 * this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright notice, 11 * this list of conditions and the following disclaimer in the documentation 12 * and/or other materials provided with the distribution. 13 * 3. The name of the author may not be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #ifdef WIN32 29 #include "talk/base/win32.h" 30 #include <shellapi.h> 31 #include <shlobj.h> 32 #include <tchar.h> 33 #endif // WIN32 34 35 #include "talk/base/common.h" 36 #include "talk/base/fileutils.h" 37 #include "talk/base/logging.h" 38 #include "talk/base/pathutils.h" 39 #include "talk/base/stringutils.h" 40 #include "talk/base/urlencode.h" 41 42 namespace talk_base { 43 44 static const char EMPTY_STR[] = ""; 45 46 // EXT_DELIM separates a file basename from extension 47 const char EXT_DELIM = '.'; 48 49 // FOLDER_DELIMS separate folder segments and the filename 50 const char* const FOLDER_DELIMS = "/\\"; 51 52 // DEFAULT_FOLDER_DELIM is the preferred delimiter for this platform 53 #if WIN32 54 const char DEFAULT_FOLDER_DELIM = '\\'; 55 #else // !WIN32 56 const char DEFAULT_FOLDER_DELIM = '/'; 57 #endif // !WIN32 58 59 /////////////////////////////////////////////////////////////////////////////// 60 // Pathname - parsing of pathnames into components, and vice versa 61 /////////////////////////////////////////////////////////////////////////////// 62 63 bool Pathname::IsFolderDelimiter(char ch) { 64 return (NULL != ::strchr(FOLDER_DELIMS, ch)); 65 } 66 67 char Pathname::DefaultFolderDelimiter() { 68 return DEFAULT_FOLDER_DELIM; 69 } 70 71 Pathname::Pathname() 72 : folder_delimiter_(DEFAULT_FOLDER_DELIM) { 73 } 74 75 Pathname::Pathname(const std::string& pathname) 76 : folder_delimiter_(DEFAULT_FOLDER_DELIM) { 77 SetPathname(pathname); 78 } 79 80 Pathname::Pathname(const std::string& folder, const std::string& filename) 81 : folder_delimiter_(DEFAULT_FOLDER_DELIM) { 82 SetPathname(folder, filename); 83 } 84 85 void Pathname::SetFolderDelimiter(char delimiter) { 86 ASSERT(IsFolderDelimiter(delimiter)); 87 folder_delimiter_ = delimiter; 88 } 89 90 void Pathname::Normalize() { 91 for (size_t i=0; i<folder_.length(); ++i) { 92 if (IsFolderDelimiter(folder_[i])) { 93 folder_[i] = folder_delimiter_; 94 } 95 } 96 } 97 98 void Pathname::clear() { 99 folder_.clear(); 100 basename_.clear(); 101 extension_.clear(); 102 } 103 104 bool Pathname::empty() const { 105 return folder_.empty() && basename_.empty() && extension_.empty(); 106 } 107 108 std::string Pathname::pathname() const { 109 std::string pathname(folder_); 110 pathname.append(basename_); 111 pathname.append(extension_); 112 if (pathname.empty()) { 113 // Instead of the empty pathname, return the current working directory. 114 pathname.push_back('.'); 115 pathname.push_back(folder_delimiter_); 116 } 117 return pathname; 118 } 119 120 std::string Pathname::url() const { 121 std::string s = "file:///"; 122 for (size_t i=0; i<folder_.length(); ++i) { 123 if (IsFolderDelimiter(folder_[i])) 124 s += '/'; 125 else 126 s += folder_[i]; 127 } 128 s += basename_; 129 s += extension_; 130 return UrlEncodeStringForOnlyUnsafeChars(s); 131 } 132 133 void Pathname::SetPathname(const std::string& pathname) { 134 std::string::size_type pos = pathname.find_last_of(FOLDER_DELIMS); 135 if (pos != std::string::npos) { 136 SetFolder(pathname.substr(0, pos + 1)); 137 SetFilename(pathname.substr(pos + 1)); 138 } else { 139 SetFolder(EMPTY_STR); 140 SetFilename(pathname); 141 } 142 } 143 144 void Pathname::SetPathname(const std::string& folder, 145 const std::string& filename) { 146 SetFolder(folder); 147 SetFilename(filename); 148 } 149 150 void Pathname::AppendPathname(const std::string& pathname) { 151 std::string full_pathname(folder_); 152 full_pathname.append(pathname); 153 SetPathname(full_pathname); 154 } 155 156 std::string Pathname::folder() const { 157 return folder_; 158 } 159 160 std::string Pathname::folder_name() const { 161 std::string::size_type pos = std::string::npos; 162 if (folder_.size() >= 2) { 163 pos = folder_.find_last_of(FOLDER_DELIMS, folder_.length() - 2); 164 } 165 if (pos != std::string::npos) { 166 return folder_.substr(pos + 1); 167 } else { 168 return folder_; 169 } 170 } 171 172 std::string Pathname::parent_folder() const { 173 std::string::size_type pos = std::string::npos; 174 if (folder_.size() >= 2) { 175 pos = folder_.find_last_of(FOLDER_DELIMS, folder_.length() - 2); 176 } 177 if (pos != std::string::npos) { 178 return folder_.substr(0, pos + 1); 179 } else { 180 return EMPTY_STR; 181 } 182 } 183 184 void Pathname::SetFolder(const std::string& folder) { 185 folder_.assign(folder); 186 // Ensure folder ends in a path delimiter 187 if (!folder_.empty() && !IsFolderDelimiter(folder_[folder_.length()-1])) { 188 folder_.push_back(folder_delimiter_); 189 } 190 } 191 192 void Pathname::AppendFolder(const std::string& folder) { 193 folder_.append(folder); 194 // Ensure folder ends in a path delimiter 195 if (!folder_.empty() && !IsFolderDelimiter(folder_[folder_.length()-1])) { 196 folder_.push_back(folder_delimiter_); 197 } 198 } 199 200 std::string Pathname::basename() const { 201 return basename_; 202 } 203 204 bool Pathname::SetBasename(const std::string& basename) { 205 if(basename.find_first_of(FOLDER_DELIMS) != std::string::npos) { 206 return false; 207 } 208 basename_.assign(basename); 209 return true; 210 } 211 212 std::string Pathname::extension() const { 213 return extension_; 214 } 215 216 bool Pathname::SetExtension(const std::string& extension) { 217 if (extension.find_first_of(FOLDER_DELIMS) != std::string::npos || 218 extension.find_first_of(EXT_DELIM, 1) != std::string::npos) { 219 return false; 220 } 221 extension_.assign(extension); 222 // Ensure extension begins with the extension delimiter 223 if (!extension_.empty() && (extension_[0] != EXT_DELIM)) { 224 extension_.insert(extension_.begin(), EXT_DELIM); 225 } 226 return true; 227 } 228 229 std::string Pathname::filename() const { 230 std::string filename(basename_); 231 filename.append(extension_); 232 return filename; 233 } 234 235 bool Pathname::SetFilename(const std::string& filename) { 236 std::string::size_type pos = filename.rfind(EXT_DELIM); 237 if ((pos == std::string::npos) || (pos == 0)) { 238 return SetExtension(EMPTY_STR) && SetBasename(filename); 239 } else { 240 return SetExtension(filename.substr(pos)) && SetBasename(filename.substr(0, pos)); 241 } 242 } 243 244 #ifdef WIN32 245 bool Pathname::GetDrive(char *drive, uint32 bytes) const { 246 return GetDrive(drive, bytes, folder_); 247 } 248 249 // static 250 bool Pathname::GetDrive(char *drive, uint32 bytes, 251 const std::string& pathname) { 252 // need at lease 4 bytes to save c: 253 if (bytes < 4 || pathname.size() < 3) { 254 return false; 255 } 256 257 memcpy(drive, pathname.c_str(), 3); 258 drive[3] = 0; 259 // sanity checking 260 return (isalpha(drive[0]) && 261 drive[1] == ':' && 262 drive[2] == '\\'); 263 } 264 #endif 265 266 /////////////////////////////////////////////////////////////////////////////// 267 268 } // namespace talk_base 269