1 /* 2 * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 #ifndef FILEUTILS_H 27 #define FILEUTILS_H 28 29 30 #include <fstream> 31 #include "SysInfo.h" 32 33 34 namespace FileUtils { 35 36 // Returns 'true' if the given character is a path separator. 37 bool isDirSeparator(const tstring::value_type c); 38 39 // checks if the file or directory exists 40 bool isFileExists(const tstring &filePath); 41 42 // checks is the specified file is a directory 43 // returns false if the path does not exist 44 bool isDirectory(const tstring &filePath); 45 46 // checks if the specified directory is not empty 47 // returns true if the path is an existing directory and 48 // it contains at least one file other than "." or "..". 49 bool isDirectoryNotEmpty(const tstring &dirPath); 50 51 // returns directory part of the path. 52 // returns empty string if the path contains only filename. 53 // if the path ends with slash/backslash, 54 // returns removeTrailingSlashes(path). 55 tstring dirname(const tstring &path); 56 57 // returns basename part of the path 58 // if the path ends with slash/backslash, returns empty string. 59 tstring basename(const tstring &path); 60 61 /** 62 * Translates forward slashes to back slashes and returns lower case version 63 * of the given string. 64 */ 65 tstring normalizePath(tstring v); 66 67 // Returns suffix of the path. If the given path has a suffix the first 68 // character of the return value is '.'. 69 // Otherwise return value if empty string. 70 tstring suffix(const tstring &path); 71 72 // combines two strings into a path 73 tstring combinePath(const tstring& parent, const tstring& child); 74 75 // removes trailing slashes and backslashes in the path if any 76 tstring removeTrailingSlash(const tstring& path); 77 78 // Creates a file with unique name in the specified base directory, 79 // throws an exception if operation fails 80 // path is constructed as <prefix><random number><suffix>. 81 // The function fails and throws exception if 'path' doesn't exist. 82 tstring createTempFile(const tstring &prefix = _T(""), 83 const tstring &suffix = _T(".tmp"), 84 const tstring &path=SysInfo::getTempDir()); 85 86 // Creates a directory with unique name in the specified base directory, 87 // throws an exception if operation fails 88 // path is constructed as <prefix><random number><suffix> 89 // The function fails and throws exception if 'path' doesn't exist. 90 tstring createTempDirectory(const tstring &prefix = _T(""), 91 const tstring &suffix = _T(".tmp"), 92 const tstring &basedir=SysInfo::getTempDir()); 93 94 // If the file referenced with "prototype" parameter DOES NOT exist, 95 // the return value is the given path. No new files created. 96 // Otherwise the function creates another file in the same directory as 97 // the given file with the same suffix and with the basename from the 98 // basename of the given file with some random chars appended to ensure 99 // created file is unique. 100 tstring createUniqueFile(const tstring &prototype); 101 102 // Creates directory and subdirectories if don't exist. 103 // Currently supports only "standard" path like "c:\bla-bla" 104 // If 'createdDirs' parameter is not NULL, the given array is appended with 105 // all subdirectories created by this function call. 106 void createDirectory(const tstring &path, tstring_array* createdDirs=0); 107 108 // copies file from fromPath to toPath. 109 // Creates output directory if doesn't exist. 110 void copyFile(const tstring& fromPath, const tstring& toPath, 111 bool failIfExists); 112 113 // moves file from fromPath to toPath. 114 // Creates output directory if doesn't exist. 115 void moveFile(const tstring& fromPath, const tstring& toPath, 116 bool failIfExists); 117 118 // Throws exception if fails to delete specified 'path'. 119 // Exits normally if 'path' doesn't exist or it has been deleted. 120 // Attempts to strip R/O attribute if delete fails and retry delete. 121 void deleteFile(const tstring &path); 122 // Returns 'false' if fails to delete specified 'path'. 123 // Returns 'true' if 'path' doesn't exist or it has been deleted. 124 // Attempts to strip R/O attribute if delete fails and retry delete. 125 bool deleteFile(const tstring &path, const std::nothrow_t &) throw(); 126 127 // Like deleteFile(), but applies to directories. 128 void deleteDirectory(const tstring &path); 129 bool deleteDirectory(const tstring &path, const std::nothrow_t &) throw(); 130 131 // Deletes all files (not subdirectories) from the specified directory. 132 // Exits normally if all files in 'dirPath' have been deleted or if 133 // 'dirPath' doesn't exist. 134 // Throws exception if 'dirPath' references existing file system object 135 // which is not a directory or when the first failure of file delete 136 // occurs. 137 void deleteFilesInDirectory(const tstring &dirPath); 138 // Deletes all files (not subdirectories) from the specified directory. 139 // Returns 'true' normally if all files in 'dirPath' have been deleted or 140 // if 'dirPath' doesn't exist. 141 // Returns 'false' if 'dirPath' references existing file system object 142 // which is not a directory or if failed to delete one ore more files in 143 // 'dirPath' directory. 144 // Doesn't abort iteration over files if the given directory after the 145 // first failure to delete a file. 146 bool deleteFilesInDirectory(const tstring &dirPath, 147 const std::nothrow_t &) throw(); 148 // Like deleteFilesInDirectory, but deletes subdirectories as well 149 void deleteDirectoryRecursive(const tstring &dirPath); 150 bool deleteDirectoryRecursive(const tstring &dirPath, 151 const std::nothrow_t &) throw(); 152 153 class DirectoryCallback { 154 public: 155 virtual ~DirectoryCallback() {}; 156 157 virtual bool onFile(const tstring& path) { 158 return true; 159 } 160 virtual bool onDirectory(const tstring& path) { 161 return true; 162 } 163 }; 164 165 // Calls the given callback for every file and subdirectory of 166 // the given directory. 167 void iterateDirectory(const tstring &dirPath, DirectoryCallback& callback); 168 169 /** 170 * Replace file suffix, example replaceSuffix("file/path.txt", ".csv") 171 * @param path file path to replace suffix 172 * @param suffix new suffix for path 173 * @return return file path with new suffix 174 */ 175 tstring replaceSuffix(const tstring& path, const tstring& suffix=tstring()); 176 177 class DirectoryIterator: DirectoryCallback { 178 public: 179 DirectoryIterator(const tstring& root=tstring()): root(root) { 180 recurse().withFiles().withFolders(); 181 } 182 183 DirectoryIterator& recurse(bool v=true) { 184 theRecurse = v; 185 return *this; 186 } 187 188 DirectoryIterator& withFiles(bool v=true) { 189 theWithFiles = v; 190 return *this; 191 } 192 193 DirectoryIterator& withFolders(bool v=true) { 194 theWithFolders = v; 195 return *this; 196 } 197 198 tstring_array findItems() { 199 tstring_array reply; 200 findItems(reply); 201 return reply; 202 } 203 204 DirectoryIterator& findItems(tstring_array& v); 205 206 private: 207 virtual bool onFile(const tstring& path); 208 virtual bool onDirectory(const tstring& path); 209 210 private: 211 bool theRecurse; 212 bool theWithFiles; 213 bool theWithFolders; 214 tstring root; 215 tstring_array items; 216 }; 217 218 // Returns array of all the files/sub-folders from the given directory, 219 // empty array if basedir is not a directory. The returned 220 // array is ordered from top down (i.e. dirs are listed first followed 221 // by subfolders and files). 222 // Order of subfolders and files is undefined 223 // but usually they are sorted by names. 224 inline tstring_array listAllContents(const tstring& basedir) { 225 return DirectoryIterator(basedir).findItems(); 226 } 227 228 // Helper to construct path from multiple components. 229 // 230 // Sample usage: 231 // Construct "c:\Program Files\Java" string from three components 232 // 233 // tstring path = FileUtils::mkpath() << _T("c:") 234 // << _T("Program Files") 235 // << _T("Java"); 236 // 237 class mkpath { 238 public: 239 operator const tstring& () const { 240 return path; 241 } 242 243 mkpath& operator << (const tstring& p) { 244 path = combinePath(path, p); 245 return *this; 246 } 247 248 // mimic std::string 249 const tstring::value_type* c_str() const { 250 return path.c_str(); 251 } 252 private: 253 tstring path; 254 }; 255 256 struct Directory { 257 Directory() { 258 } 259 260 Directory(const tstring &parent, 261 const tstring &subdir) : parent(parent), subdir(subdir) { 262 } 263 264 operator tstring () const { 265 return getPath(); 266 } 267 268 tstring getPath() const { 269 return combinePath(parent, subdir); 270 } 271 272 bool empty() const { 273 return (parent.empty() && subdir.empty()); 274 } 275 276 tstring parent; 277 tstring subdir; 278 }; 279 280 // Deletes list of files and directories in batch mode. 281 // Registered files and directories are deleted when destructor is called. 282 // Order or delete operations is following: 283 // - delete items registered with appendFile() calls; 284 // - delete items registered with appendAllFilesInDirectory() calls; 285 // - delete items registered with appendRecursiveDirectory() calls; 286 // - delete items registered with appendEmptyDirectory() calls. 287 class Deleter { 288 public: 289 Deleter() { 290 } 291 292 ~Deleter() { 293 execute(); 294 } 295 296 typedef std::pair<tstring, int> Path; 297 typedef std::vector<Path> Paths; 298 299 /** 300 * Appends all records from the given deleter Deleter into this Deleter 301 * instance. On success array with records in the passed in Deleter 302 * instance is emptied. 303 */ 304 Deleter& appendFrom(Deleter& other) { 305 Paths tmp(paths); 306 tmp.insert(tmp.end(), other.paths.begin(), other.paths.end()); 307 Paths empty; 308 other.paths.swap(empty); 309 paths.swap(tmp); 310 return *this; 311 } 312 313 // Schedule file for deletion. 314 Deleter& appendFile(const tstring& path); 315 316 // Schedule files for deletion. 317 template <class It> 318 Deleter& appendFiles(It b, It e) { 319 for (It it = b; it != e; ++it) { 320 appendFile(*it); 321 } 322 return *this; 323 } 324 325 // Schedule files for deletion in the given directory. 326 template <class It> 327 Deleter& appendFiles(const tstring& dirname, It b, It e) { 328 for (It it = b; it != e; ++it) { 329 appendFile(FileUtils::mkpath() << dirname << *it); 330 } 331 return *this; 332 } 333 334 // Schedule empty directory for deletion with empty roots 335 // (up to Directory.parent). 336 Deleter& appendEmptyDirectory(const Directory& dir); 337 338 // Schedule empty directory for deletion without roots. 339 // This is a particular case of 340 // appendEmptyDirectory(const Directory& dir) 341 // with Directory(dirname(path), basename(path)). 342 Deleter& appendEmptyDirectory(const tstring& path); 343 344 // Schedule all file from the given directory for deletion. 345 Deleter& appendAllFilesInDirectory(const tstring& path); 346 347 // Schedule directory for recursive deletion. 348 Deleter& appendRecursiveDirectory(const tstring& path); 349 350 void cancel() { 351 paths.clear(); 352 } 353 354 // Deletes scheduled files and directories. After this function 355 // is called internal list of scheduled items is emptied. 356 void execute(); 357 358 private: 359 Paths paths; 360 }; 361 362 363 /** 364 * Helper to write chunks of data into binary file. 365 * Creates temporary file in the same folder with destination file. 366 * All subsequent requests to save data chunks are redirected to temporary 367 * file. finalize() method closes temporary file stream and renames 368 * temporary file. 369 * If finalize() method is not called, temporary file is deleted in 370 * ~FileWriter(), destination file is not touched. 371 */ 372 class FileWriter { 373 public: 374 explicit FileWriter(const tstring& path); 375 376 FileWriter& write(const void* buf, size_t bytes); 377 378 template <class Ctnr> 379 FileWriter& write(const Ctnr& buf) { 380 return write(buf.data(), 381 buf.size() * sizeof(typename Ctnr::value_type)); 382 } 383 384 void finalize(); 385 386 private: 387 // Not accessible by design! 388 FileWriter& write(const std::wstring& str); 389 390 private: 391 tstring tmpFile; 392 Deleter cleaner; 393 std::ofstream tmp; 394 tstring dstPath; 395 }; 396 } // FileUtils 397 398 #endif // FILEUTILS_H