--- /dev/null 2019-11-18 21:17:25.000000000 -0500 +++ new/src/jdk.incubator.jpackage/windows/native/libjpackage/FileUtils.h 2019-11-18 21:17:22.525608100 -0500 @@ -0,0 +1,398 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef FILEUTILS_H +#define FILEUTILS_H + + +#include +#include "SysInfo.h" + + +namespace FileUtils { + + // Returns 'true' if the given character is a path separator. + bool isDirSeparator(const tstring::value_type c); + + // checks if the file or directory exists + bool isFileExists(const tstring &filePath); + + // checks is the specified file is a directory + // returns false if the path does not exist + bool isDirectory(const tstring &filePath); + + // checks if the specified directory is not empty + // returns true if the path is an existing directory and + // it contains at least one file other than "." or "..". + bool isDirectoryNotEmpty(const tstring &dirPath); + + // returns directory part of the path. + // returns empty string if the path contains only filename. + // if the path ends with slash/backslash, + // returns removeTrailingSlashes(path). + tstring dirname(const tstring &path); + + // returns basename part of the path + // if the path ends with slash/backslash, returns empty string. + tstring basename(const tstring &path); + + /** + * Translates forward slashes to back slashes and returns lower case version + * of the given string. + */ + tstring normalizePath(tstring v); + + // Returns suffix of the path. If the given path has a suffix the first + // character of the return value is '.'. + // Otherwise return value if empty string. + tstring suffix(const tstring &path); + + // combines two strings into a path + tstring combinePath(const tstring& parent, const tstring& child); + + // removes trailing slashes and backslashes in the path if any + tstring removeTrailingSlash(const tstring& path); + + // Creates a file with unique name in the specified base directory, + // throws an exception if operation fails + // path is constructed as . + // The function fails and throws exception if 'path' doesn't exist. + tstring createTempFile(const tstring &prefix = _T(""), + const tstring &suffix = _T(".tmp"), + const tstring &path=SysInfo::getTempDir()); + + // Creates a directory with unique name in the specified base directory, + // throws an exception if operation fails + // path is constructed as + // The function fails and throws exception if 'path' doesn't exist. + tstring createTempDirectory(const tstring &prefix = _T(""), + const tstring &suffix = _T(".tmp"), + const tstring &basedir=SysInfo::getTempDir()); + + // If the file referenced with "prototype" parameter DOES NOT exist, + // the return value is the given path. No new files created. + // Otherwise the function creates another file in the same directory as + // the given file with the same suffix and with the basename from the + // basename of the given file with some random chars appended to ensure + // created file is unique. + tstring createUniqueFile(const tstring &prototype); + + // Creates directory and subdirectories if don't exist. + // Currently supports only "standard" path like "c:\bla-bla" + // If 'createdDirs' parameter is not NULL, the given array is appended with + // all subdirectories created by this function call. + void createDirectory(const tstring &path, tstring_array* createdDirs=0); + + // copies file from fromPath to toPath. + // Creates output directory if doesn't exist. + void copyFile(const tstring& fromPath, const tstring& toPath, + bool failIfExists); + + // moves file from fromPath to toPath. + // Creates output directory if doesn't exist. + void moveFile(const tstring& fromPath, const tstring& toPath, + bool failIfExists); + + // Throws exception if fails to delete specified 'path'. + // Exits normally if 'path' doesn't exist or it has been deleted. + // Attempts to strip R/O attribute if delete fails and retry delete. + void deleteFile(const tstring &path); + // Returns 'false' if fails to delete specified 'path'. + // Returns 'true' if 'path' doesn't exist or it has been deleted. + // Attempts to strip R/O attribute if delete fails and retry delete. + bool deleteFile(const tstring &path, const std::nothrow_t &) throw(); + + // Like deleteFile(), but applies to directories. + void deleteDirectory(const tstring &path); + bool deleteDirectory(const tstring &path, const std::nothrow_t &) throw(); + + // Deletes all files (not subdirectories) from the specified directory. + // Exits normally if all files in 'dirPath' have been deleted or if + // 'dirPath' doesn't exist. + // Throws exception if 'dirPath' references existing file system object + // which is not a directory or when the first failure of file delete + // occurs. + void deleteFilesInDirectory(const tstring &dirPath); + // Deletes all files (not subdirectories) from the specified directory. + // Returns 'true' normally if all files in 'dirPath' have been deleted or + // if 'dirPath' doesn't exist. + // Returns 'false' if 'dirPath' references existing file system object + // which is not a directory or if failed to delete one ore more files in + // 'dirPath' directory. + // Doesn't abort iteration over files if the given directory after the + // first failure to delete a file. + bool deleteFilesInDirectory(const tstring &dirPath, + const std::nothrow_t &) throw(); + // Like deleteFilesInDirectory, but deletes subdirectories as well + void deleteDirectoryRecursive(const tstring &dirPath); + bool deleteDirectoryRecursive(const tstring &dirPath, + const std::nothrow_t &) throw(); + + class DirectoryCallback { + public: + virtual ~DirectoryCallback() {}; + + virtual bool onFile(const tstring& path) { + return true; + } + virtual bool onDirectory(const tstring& path) { + return true; + } + }; + + // Calls the given callback for every file and subdirectory of + // the given directory. + void iterateDirectory(const tstring &dirPath, DirectoryCallback& callback); + + /** + * Replace file suffix, example replaceSuffix("file/path.txt", ".csv") + * @param path file path to replace suffix + * @param suffix new suffix for path + * @return return file path with new suffix + */ + tstring replaceSuffix(const tstring& path, const tstring& suffix=tstring()); + + class DirectoryIterator: DirectoryCallback { + public: + DirectoryIterator(const tstring& root=tstring()): root(root) { + recurse().withFiles().withFolders(); + } + + DirectoryIterator& recurse(bool v=true) { + theRecurse = v; + return *this; + } + + DirectoryIterator& withFiles(bool v=true) { + theWithFiles = v; + return *this; + } + + DirectoryIterator& withFolders(bool v=true) { + theWithFolders = v; + return *this; + } + + tstring_array findItems() { + tstring_array reply; + findItems(reply); + return reply; + } + + DirectoryIterator& findItems(tstring_array& v); + + private: + virtual bool onFile(const tstring& path); + virtual bool onDirectory(const tstring& path); + + private: + bool theRecurse; + bool theWithFiles; + bool theWithFolders; + tstring root; + tstring_array items; + }; + + // Returns array of all the files/sub-folders from the given directory, + // empty array if basedir is not a directory. The returned + // array is ordered from top down (i.e. dirs are listed first followed + // by subfolders and files). + // Order of subfolders and files is undefined + // but usually they are sorted by names. + inline tstring_array listAllContents(const tstring& basedir) { + return DirectoryIterator(basedir).findItems(); + } + + // Helper to construct path from multiple components. + // + // Sample usage: + // Construct "c:\Program Files\Java" string from three components + // + // tstring path = FileUtils::mkpath() << _T("c:") + // << _T("Program Files") + // << _T("Java"); + // + class mkpath { + public: + operator const tstring& () const { + return path; + } + + mkpath& operator << (const tstring& p) { + path = combinePath(path, p); + return *this; + } + + // mimic std::string + const tstring::value_type* c_str() const { + return path.c_str(); + } + private: + tstring path; + }; + + struct Directory { + Directory() { + } + + Directory(const tstring &parent, + const tstring &subdir) : parent(parent), subdir(subdir) { + } + + operator tstring () const { + return getPath(); + } + + tstring getPath() const { + return combinePath(parent, subdir); + } + + bool empty() const { + return (parent.empty() && subdir.empty()); + } + + tstring parent; + tstring subdir; + }; + + // Deletes list of files and directories in batch mode. + // Registered files and directories are deleted when destructor is called. + // Order or delete operations is following: + // - delete items registered with appendFile() calls; + // - delete items registered with appendAllFilesInDirectory() calls; + // - delete items registered with appendRecursiveDirectory() calls; + // - delete items registered with appendEmptyDirectory() calls. + class Deleter { + public: + Deleter() { + } + + ~Deleter() { + execute(); + } + + typedef std::pair Path; + typedef std::vector Paths; + + /** + * Appends all records from the given deleter Deleter into this Deleter + * instance. On success array with records in the passed in Deleter + * instance is emptied. + */ + Deleter& appendFrom(Deleter& other) { + Paths tmp(paths); + tmp.insert(tmp.end(), other.paths.begin(), other.paths.end()); + Paths empty; + other.paths.swap(empty); + paths.swap(tmp); + return *this; + } + + // Schedule file for deletion. + Deleter& appendFile(const tstring& path); + + // Schedule files for deletion. + template + Deleter& appendFiles(It b, It e) { + for (It it = b; it != e; ++it) { + appendFile(*it); + } + return *this; + } + + // Schedule files for deletion in the given directory. + template + Deleter& appendFiles(const tstring& dirname, It b, It e) { + for (It it = b; it != e; ++it) { + appendFile(FileUtils::mkpath() << dirname << *it); + } + return *this; + } + + // Schedule empty directory for deletion with empty roots + // (up to Directory.parent). + Deleter& appendEmptyDirectory(const Directory& dir); + + // Schedule empty directory for deletion without roots. + // This is a particular case of + // appendEmptyDirectory(const Directory& dir) + // with Directory(dirname(path), basename(path)). + Deleter& appendEmptyDirectory(const tstring& path); + + // Schedule all file from the given directory for deletion. + Deleter& appendAllFilesInDirectory(const tstring& path); + + // Schedule directory for recursive deletion. + Deleter& appendRecursiveDirectory(const tstring& path); + + void cancel() { + paths.clear(); + } + + // Deletes scheduled files and directories. After this function + // is called internal list of scheduled items is emptied. + void execute(); + + private: + Paths paths; + }; + + + /** + * Helper to write chunks of data into binary file. + * Creates temporary file in the same folder with destination file. + * All subsequent requests to save data chunks are redirected to temporary + * file. finalize() method closes temporary file stream and renames + * temporary file. + * If finalize() method is not called, temporary file is deleted in + * ~FileWriter(), destination file is not touched. + */ + class FileWriter { + public: + explicit FileWriter(const tstring& path); + + FileWriter& write(const void* buf, size_t bytes); + + template + FileWriter& write(const Ctnr& buf) { + return write(buf.data(), + buf.size() * sizeof(typename Ctnr::value_type)); + } + + void finalize(); + + private: + // Not accessible by design! + FileWriter& write(const std::wstring& str); + + private: + tstring tmpFile; + Deleter cleaner; + std::ofstream tmp; + tstring dstPath; + }; +} // FileUtils + +#endif // FILEUTILS_H