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