1 /* 2 * libjingle 3 * Copyright 2004--2006, 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 #include <cassert> 29 30 #ifdef WIN32 31 #include "talk/base/win32.h" 32 #endif 33 34 #include "talk/base/pathutils.h" 35 #include "talk/base/fileutils.h" 36 #include "talk/base/stringutils.h" 37 #include "talk/base/stream.h" 38 39 #ifdef WIN32 40 #include "talk/base/win32filesystem.h" 41 #else 42 #include "talk/base/unixfilesystem.h" 43 #endif 44 45 #ifndef WIN32 46 #define MAX_PATH 260 47 #endif 48 49 namespace talk_base { 50 51 ////////////////////////// 52 // Directory Iterator // 53 ////////////////////////// 54 55 // A DirectoryIterator is created with a given directory. It originally points 56 // to the first file in the directory, and can be advanecd with Next(). This 57 // allows you to get information about each file. 58 59 // Constructor 60 DirectoryIterator::DirectoryIterator() 61 #ifdef _WIN32 62 : handle_(INVALID_HANDLE_VALUE) { 63 #else 64 : dir_(NULL), dirent_(NULL) { 65 #endif 66 } 67 68 // Destructor 69 DirectoryIterator::~DirectoryIterator() { 70 #ifdef WIN32 71 if (handle_ != INVALID_HANDLE_VALUE) 72 ::FindClose(handle_); 73 #else 74 if (dir_) 75 closedir(dir_); 76 #endif 77 } 78 79 // Starts traversing a directory. 80 // dir is the directory to traverse 81 // returns true if the directory exists and is valid 82 bool DirectoryIterator::Iterate(const Pathname &dir) { 83 directory_ = dir.pathname(); 84 #ifdef WIN32 85 if (handle_ != INVALID_HANDLE_VALUE) 86 ::FindClose(handle_); 87 std::string d = dir.pathname() + '*'; 88 handle_ = ::FindFirstFile(ToUtf16(d).c_str(), &data_); 89 if (handle_ == INVALID_HANDLE_VALUE) 90 return false; 91 #else 92 if (dir_ != NULL) 93 closedir(dir_); 94 dir_ = ::opendir(directory_.c_str()); 95 if (dir_ == NULL) 96 return false; 97 dirent_ = readdir(dir_); 98 if (dirent_ == NULL) 99 return false; 100 101 if (::stat(std::string(directory_ + Name()).c_str(), &stat_) != 0) 102 return false; 103 #endif 104 return true; 105 } 106 107 // Advances to the next file 108 // returns true if there were more files in the directory. 109 bool DirectoryIterator::Next() { 110 #ifdef WIN32 111 return ::FindNextFile(handle_, &data_) == TRUE; 112 #else 113 dirent_ = ::readdir(dir_); 114 if (dirent_ == NULL) 115 return false; 116 117 return ::stat(std::string(directory_ + Name()).c_str(), &stat_) == 0; 118 #endif 119 } 120 121 // returns true if the file currently pointed to is a directory 122 bool DirectoryIterator::IsDirectory() const { 123 #ifdef WIN32 124 return (data_.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != FALSE; 125 #else 126 return S_ISDIR(stat_.st_mode); 127 #endif 128 } 129 130 // returns the name of the file currently pointed to 131 std::string DirectoryIterator::Name() const { 132 #ifdef WIN32 133 return ToUtf8(data_.cFileName); 134 #else 135 assert(dirent_ != NULL); 136 return dirent_->d_name; 137 #endif 138 } 139 140 // returns the size of the file currently pointed to 141 size_t DirectoryIterator::FileSize() const { 142 #ifndef WIN32 143 return stat_.st_size; 144 #else 145 return data_.nFileSizeLow; 146 #endif 147 } 148 149 // returns the last modified time of this file 150 time_t DirectoryIterator::FileModifyTime() const { 151 #ifdef WIN32 152 time_t val; 153 FileTimeToUnixTime(data_.ftLastWriteTime, &val); 154 return val; 155 #else 156 return stat_.st_mtime; 157 #endif 158 } 159 160 FilesystemInterface* Filesystem::default_filesystem_ = NULL; 161 162 FilesystemInterface *Filesystem::EnsureDefaultFilesystem() { 163 if (!default_filesystem_) { 164 #ifdef WIN32 165 default_filesystem_ = new Win32Filesystem(); 166 #else 167 default_filesystem_ = new UnixFilesystem(); 168 #endif 169 } 170 return default_filesystem_; 171 } 172 173 bool FilesystemInterface::CopyFolder(const Pathname &old_path, 174 const Pathname &new_path) { 175 bool success = true; 176 VERIFY(IsFolder(old_path)); 177 Pathname new_dir; 178 new_dir.SetFolder(new_path.pathname()); 179 Pathname old_dir; 180 old_dir.SetFolder(old_path.pathname()); 181 if (!CreateFolder(new_dir)) 182 return false; 183 DirectoryIterator *di = IterateDirectory(); 184 if (!di) 185 return false; 186 if (di->Iterate(old_dir.pathname())) { 187 do { 188 if (di->Name() == "." || di->Name() == "..") 189 continue; 190 Pathname source; 191 Pathname dest; 192 source.SetFolder(old_dir.pathname()); 193 dest.SetFolder(new_path.pathname()); 194 source.SetFilename(di->Name()); 195 dest.SetFilename(di->Name()); 196 if (!CopyFileOrFolder(source, dest)) 197 success = false; 198 } while (di->Next()); 199 } 200 delete di; 201 return success; 202 } 203 204 bool FilesystemInterface::DeleteFolderContents(const Pathname &folder) { 205 bool success = true; 206 VERIFY(IsFolder(folder)); 207 DirectoryIterator *di = IterateDirectory(); 208 if (!di) 209 return false; 210 if (di->Iterate(folder)) { 211 do { 212 if (di->Name() == "." || di->Name() == "..") 213 continue; 214 Pathname subdir; 215 subdir.SetFolder(folder.pathname()); 216 if (di->IsDirectory()) { 217 subdir.AppendFolder(di->Name()); 218 if (!DeleteFolderAndContents(subdir)) { 219 success = false; 220 } 221 } else { 222 subdir.SetFilename(di->Name()); 223 if (!DeleteFile(subdir)) { 224 success = false; 225 } 226 } 227 } while (di->Next()); 228 } 229 delete di; 230 return success; 231 } 232 233 bool FilesystemInterface::CleanAppTempFolder() { 234 Pathname path; 235 if (!GetAppTempFolder(&path)) 236 return false; 237 if (IsAbsent(path)) 238 return true; 239 if (!IsTemporaryPath(path)) { 240 ASSERT(false); 241 return false; 242 } 243 return DeleteFolderContents(path); 244 } 245 246 Pathname Filesystem::GetCurrentDirectory() { 247 return EnsureDefaultFilesystem()->GetCurrentDirectory(); 248 } 249 250 bool CreateUniqueFile(Pathname& path, bool create_empty) { 251 LOG(LS_INFO) << "Path " << path.pathname() << std::endl; 252 // If no folder is supplied, use the temporary folder 253 if (path.folder().empty()) { 254 Pathname temporary_path; 255 if (!Filesystem::GetTemporaryFolder(temporary_path, true, NULL)) { 256 printf("Get temp failed\n"); 257 return false; 258 } 259 path.SetFolder(temporary_path.pathname()); 260 } 261 262 // If no filename is supplied, use a temporary name 263 if (path.filename().empty()) { 264 std::string folder(path.folder()); 265 std::string filename = Filesystem::TempFilename(folder, "gt"); 266 path.SetPathname(filename); 267 if (!create_empty) { 268 Filesystem::DeleteFile(path.pathname()); 269 } 270 return true; 271 } 272 273 // Otherwise, create a unique name based on the given filename 274 // foo.txt -> foo-N.txt 275 const std::string basename = path.basename(); 276 const size_t MAX_VERSION = 100; 277 size_t version = 0; 278 while (version < MAX_VERSION) { 279 std::string pathname = path.pathname(); 280 281 if (!Filesystem::IsFile(pathname)) { 282 if (create_empty) { 283 FileStream* fs = Filesystem::OpenFile(pathname, "w"); 284 delete fs; 285 } 286 return true; 287 } 288 version += 1; 289 char version_base[MAX_PATH]; 290 sprintfn(version_base, ARRAY_SIZE(version_base), "%s-%u", 291 basename.c_str(), version); 292 path.SetBasename(version_base); 293 } 294 return true; 295 } 296 297 } // namespace talk_base 298