Răsfoiți Sursa

Implement FileSystem methods on Unix

Also, this commit makes some of the Windows-only methods cross-platform
to factorize code.
Marc Legendre 9 ani în urmă
părinte
comite
6d91f2be2f

+ 2 - 0
Source/BansheeUtility/CMakeSources.cmake

@@ -28,6 +28,7 @@ set(BS_BANSHEEUTILITY_SRC_WIN32
 
 set(BS_BANSHEEUTILITY_SRC_UNIX
 	"Source/Unix/BsUnixCrashHandler.cpp"
+	"Source/Unix/BsUnixFileSystem.cpp"
 	"Source/Unix/BsUnixPlatformUtility.cpp"
 )
 
@@ -70,6 +71,7 @@ set(BS_BANSHEEUTILITY_INC_FILESYSTEM
 
 set(BS_BANSHEEUTILITY_SRC_FILESYSTEM
 	"Source/BsDataStream.cpp"
+	"Source/BsFileSystem.cpp"
 	"Source/BsPath.cpp"
 )
 

+ 18 - 9
Source/BansheeUtility/Include/BsFileSystem.h

@@ -23,7 +23,7 @@ namespace BansheeEngine
 		static SPtr<DataStream> openFile(const Path& fullPath, bool readOnly = true);
 
 		/**
-		 * Opens a file and returns a data stream capable of reading and writing to that file. If file doesn't exist new 
+		 * Opens a file and returns a data stream capable of reading and writing to that file. If file doesn't exist new
 		 * one will be created.
 		 *
 		 * @param[in]	fullPath	Full path to a file.
@@ -41,7 +41,7 @@ namespace BansheeEngine
 		 * Deletes a file or a folder at the specified path.
 		 *
 		 * @param[in]	fullPath   	Full path to a file or a folder..
-		 * @param[in]	recursively	(optional) If true, folders will have their contents deleted as well. Otherwise an 
+		 * @param[in]	recursively	(optional) If true, folders will have their contents deleted as well. Otherwise an
 		 *							exception will be thrown for non-empty folders.
 		 */
 		static void remove(const Path& fullPath, bool recursively = true);
@@ -51,17 +51,17 @@ namespace BansheeEngine
 		 *
 		 * @param[in]	oldPath			 	Full path to the old file/folder.
 		 * @param[in]	newPath			 	Full path to the new file/folder.
-		 * @param[in]	overwriteExisting	(optional) If true, any existing file/folder at the new location will be 
+		 * @param[in]	overwriteExisting	(optional) If true, any existing file/folder at the new location will be
 		 *									overwritten, otherwise an exception will be thrown if a file/folder already exists.
 		 */
 		static void move(const Path& oldPath, const Path& newPath, bool overwriteExisting = true);
 
 		/**
-		 * Makes a copy of a file or a folder in the specified path. 
+		 * Makes a copy of a file or a folder in the specified path.
 		 *
 		 * @param[in]	oldPath			 	Full path to the old file/folder.
 		 * @param[in]	newPath			 	Full path to the new file/folder.
-		 * @param[in]	overwriteExisting	(optional) If true, any existing file/folder at the new location will be 
+		 * @param[in]	overwriteExisting	(optional) If true, any existing file/folder at the new location will be
 		 *									overwritten, otherwise an exception will be thrown if a file/folder already exists.
 		 */
 		static void copy(const Path& oldPath, const Path& newPath, bool overwriteExisting = true);
@@ -104,15 +104,15 @@ namespace BansheeEngine
 		static void getChildren(const Path& dirPath, Vector<Path>& files, Vector<Path>& directories);
 
 		/**
-		 * Iterates over all files and directories in the specified folder and calls the provided callback when a 
+		 * Iterates over all files and directories in the specified folder and calls the provided callback when a
 		 * file/folder is iterated over.
 		 *
 		 * @param[in]	dirPath			Directory over which to iterate
 		 * @param[in]	fileCallback	Callback to call whenever a file is found. If callback returns false iteration stops. Can be null.
 		 * @param[in]	dirCallback		Callback to call whenever a directory is found. If callback returns false iteration stops. Can be null.
-		 * @param[in]	recursive		If false then only the direct children of the provided folder will be iterated over, 
+		 * @param[in]	recursive		If false then only the direct children of the provided folder will be iterated over,
 		 *								and if true then child directories will be recursively visited as well.
-		 * @return						True if iteration finished iterating over all files/folders, or false if it was 
+		 * @return						True if iteration finished iterating over all files/folders, or false if it was
 		 *								interrupted by a callback returning false.
 		 */
 		static bool iterate(const Path& dirPath, std::function<bool(const Path&)> fileCallback,
@@ -130,7 +130,16 @@ namespace BansheeEngine
 
 		/** Returns the path to a directory where temporary files may be stored. */
 		static Path getTempDirectoryPath();
+
+	private:
+		/** Copy a single file. Internal function used by copy(). */
+		static void copyFile(const Path& oldPath, const Path& newPath);
+		/** Remove a single file. Internal function used by remove(). */
+		static void removeFile(const Path& path);
+		/** Move a single file. Internal function used by move(). */
+		static void moveFile(const Path& oldPath, const Path& newPath);
+
 	};
 
 	/** @} */
-}
+}

+ 127 - 0
Source/BansheeUtility/Source/BsFileSystem.cpp

@@ -0,0 +1,127 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+
+#include "BsFileSystem.h"
+
+#include "BsDebug.h"
+
+namespace BansheeEngine
+{
+
+	void FileSystem::copy(const Path& oldPath, const Path& newPath, bool overwriteExisting)
+	{
+		Stack<std::tuple<Path, Path>> todo;
+		todo.push(std::make_tuple(oldPath, newPath));
+
+		while (!todo.empty())
+		{
+			auto current = todo.top();
+			todo.pop();
+
+			Path sourcePath = std::get<0>(current);
+			if (!FileSystem::exists(sourcePath))
+				continue;
+
+			bool srcIsFile = FileSystem::isFile(sourcePath);
+			Path destinationPath = std::get<1>(current);
+			bool destExists = FileSystem::exists(destinationPath);
+
+			if (destExists)
+			{
+				if (FileSystem::isFile(destinationPath))
+				{
+					if (overwriteExisting)
+						FileSystem::remove(destinationPath);
+					else
+					{
+						LOGWRN("Copy operation failed because another file already exists at the new path: \"" + destinationPath.toString() + "\"");
+						return;
+					}
+				}
+			}
+
+			// bool destIsFile = !destinationPath.getWExtension().empty();
+
+			// if (!srcIsFile && destIsFile)
+			// {
+			// 	LOGWRN("Cannot copy a source folder to a destination file.");
+			// 	return;
+			// }
+			// else if (srcIsFile && !destIsFile)
+			// {
+			// 	Path destinationFilePath = destinationPath;
+			// 	destinationFilePath.append(sourcePath.getWTail());
+
+			// 	FileSystem::copyFile(sourcePath, destinationFilePath);
+			// }
+			if (srcIsFile)// && destIsFile)
+			{
+				FileSystem::copyFile(sourcePath, destinationPath);
+			}
+			else // if (!srcIsFile)// && !destIsFile)
+			{
+				if (!destExists)
+					FileSystem::createDir(destinationPath);
+
+				Vector<Path> files;
+				Vector<Path> directories;
+				getChildren(destinationPath, files, directories);
+
+				for (auto& file : files)
+				{
+					Path fileDestPath = destinationPath;
+					fileDestPath.append(file.getWTail());
+
+					todo.push(std::make_tuple(file, fileDestPath));
+				}
+
+				for (auto& dir : directories)
+				{
+					Path dirDestPath = destinationPath;
+					dirDestPath.append(dir.getWTail());
+
+					todo.push(std::make_tuple(dir, dirDestPath));
+				}
+			}
+		}
+	}
+
+	void FileSystem::remove(const Path& path, bool recursively)
+	{
+		if (!FileSystem::exists(path))
+			return;
+
+		if (recursively)
+		{
+			Vector<Path> files;
+			Vector<Path> directories;
+
+			getChildren(path, files, directories);
+
+			for (auto& file : files)
+				remove(file, false);
+
+			for (auto& dir : directories)
+				remove(dir, true);
+		}
+
+		FileSystem::removeFile(path);
+	}
+
+	void FileSystem::move(const Path& oldPath, const Path& newPath, bool overwriteExisting)
+	{
+		if (FileSystem::exists(newPath))
+		{
+			if (overwriteExisting)
+				FileSystem::remove(newPath);
+			else
+			{
+				LOGWRN("Move operation failed because another file already exists at the new path: \"" + newPath.toString() + "\"");
+				return;
+			}
+		}
+
+		FileSystem::moveFile(oldPath, newPath);
+	}
+
+}

+ 300 - 0
Source/BansheeUtility/Source/Unix/BsUnixFileSystem.cpp

@@ -0,0 +1,300 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "BsFileSystem.h"
+
+#include "BsException.h"
+#include "BsDataStream.h"
+#include "BsDebug.h"
+
+#include <dirent.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <climits>
+#include <cstring>
+#include <cstdio>
+#include <cstdlib>
+#include <fstream>
+
+#define HANDLE_PATH_ERROR(path__, errno__) \
+	LOGERR(String(__FUNCTION__) + ": " + (path__) + ": " + (strerror(errno__)));
+
+namespace BansheeEngine
+{
+	bool unix_pathExists(const String& path)
+	{
+		struct stat st_buf;
+		if (stat(path.c_str(), &st_buf) == 0)
+			return true;
+		else
+			if (errno == ENOENT)    // No such file or directory
+				return false;
+			else
+			{
+				HANDLE_PATH_ERROR(path, errno);
+				return false;
+			}
+	}
+
+	bool unix_stat(const String& path, struct stat *st_buf)
+	{
+		if (stat(path.c_str(), st_buf) != 0)
+		{
+			HANDLE_PATH_ERROR(path, errno);
+			return false;
+		}
+		return true;
+	}
+
+	bool unix_isFile(const String& path)
+	{
+		struct stat st_buf;
+		if (unix_stat(path, &st_buf))
+		{
+			return S_ISREG(st_buf.st_mode);
+		}
+	}
+
+	bool unix_isDirectory(const String& path)
+	{
+		struct stat st_buf;
+		if (unix_stat(path, &st_buf))
+		{
+			return S_ISDIR(st_buf.st_mode);
+		}
+		return false;
+	}
+
+	// bool unix_createFile(const String& path)
+	// {
+	// 	int fd = open(path.c_str(), O_RDWR|O_CREAT, 0755);
+
+	// 	if (fd != -1)
+	// 		close(fd2);
+
+	// 	return ???;
+	// }
+
+	bool unix_createDirectory(const String& path)
+	{
+		if (unix_pathExists(path) && unix_isDirectory(path))
+			return false;
+
+		if (mkdir(path.c_str(), 0755))
+		{
+			HANDLE_PATH_ERROR(path, errno);
+			return false;
+		}
+
+		return true;
+	}
+
+	void FileSystem::removeFile(const Path& path)
+	{
+		String pathStr = path.toString();
+		if (unix_isDirectory(pathStr))
+		{
+			if (rmdir(pathStr.c_str()))
+				HANDLE_PATH_ERROR(pathStr, errno);
+		}
+		else
+		{
+			if (unlink(pathStr.c_str()))
+				HANDLE_PATH_ERROR(pathStr, errno);
+		}
+	}
+
+	void FileSystem::copyFile(const Path& source, const Path& destination)
+	{
+		std::ifstream sourceStream(source.toString().c_str(), std::ios::binary);
+		std::ofstream destinationStream(destination.toString().c_str(), std::ios::binary);
+
+		destinationStream << sourceStream.rdbuf();
+		// std::cout << "copy: " << source.toString() << " to " << destination.toString() << std::endl;
+		sourceStream.close();
+		destinationStream.close();
+	}
+
+	void FileSystem::moveFile(const Path& oldPath, const Path& newPath)
+	{
+		String oldPathStr = oldPath.toString();
+		String newPathStr = newPath.toString();
+		if (std::rename(oldPathStr.c_str(), newPathStr.c_str()) == -1)
+		{
+			LOGERR(String(__FUNCTION__) + ": renaming " + oldPathStr + " to " + newPathStr +
+			       ": " + strerror(errno));
+		}
+	}
+
+	SPtr<DataStream> FileSystem::openFile(const Path& path, bool readOnly)
+	{
+		String pathString = path.toString();
+		// const char_t* pathString = pathString.c_str();
+
+		// if (!pathExists(pathString) || !isFile(pathString))
+		// {
+		// 	LOGWRN("Attempting to open a file that doesn't exist: " + path.toString());
+		// 	return nullptr;
+		// }
+
+		DataStream::AccessMode accessMode = DataStream::READ;
+		if (!readOnly)
+			accessMode = (DataStream::AccessMode)(accessMode | (UINT32)DataStream::WRITE);
+
+		return bs_shared_ptr_new<FileDataStream>(path, accessMode, true);
+	}
+
+	SPtr<DataStream> FileSystem::createAndOpenFile(const Path& path)
+	{
+		return bs_shared_ptr_new<FileDataStream>(path, DataStream::AccessMode::WRITE, true);
+	}
+
+	UINT64 FileSystem::getFileSize(const Path& path)
+	{
+		struct stat st_buf;
+
+		if (stat(path.toString().c_str(), &st_buf) == 0)
+		{
+			return st_buf.st_size;
+		}
+		else
+		{
+			HANDLE_PATH_ERROR(path.toString(), errno);
+			return -1;
+		}
+	}
+
+	bool FileSystem::exists(const Path& path)
+	{
+		return unix_pathExists(path.toString());
+	}
+
+	bool FileSystem::isFile(const Path& path)
+	{
+		String pathStr = path.toString();
+		return unix_pathExists(pathStr) && unix_isFile(pathStr);
+	}
+
+	bool FileSystem::isDirectory(const Path& path)
+	{
+		String pathStr = path.toString();
+		return unix_pathExists(pathStr) && unix_isDirectory(pathStr);
+	}
+
+	void FileSystem::createDir(const Path& path)
+	{
+		Path parentPath = path;
+		while (!exists(parentPath) && parentPath.getNumDirectories() > 0)
+		{
+			parentPath = parentPath.getParent();
+		}
+
+		for (UINT32 i = parentPath.getNumDirectories(); i < path.getNumDirectories(); i++)
+		{
+			parentPath.append(path[i]);
+			unix_createDirectory(parentPath.toString());
+		}
+	}
+
+	void FileSystem::getChildren(const Path& dirPath, Vector<Path>& files, Vector<Path>& directories)
+	{
+		const String pathStr = dirPath.toString();
+
+		if (unix_isFile(pathStr))
+			return;
+
+
+		DIR *dp = opendir(pathStr.c_str());
+		if (dp == NULL)
+		{
+			HANDLE_PATH_ERROR(pathStr, errno);
+			return;
+		}
+
+		struct dirent *ep;
+		while ( (ep = readdir(dp)) )
+		{
+			const String filename(ep->d_name);
+			if (filename != "." && filename != "..")
+			{
+				if (unix_isDirectory(pathStr + "/" + filename))
+				{
+					directories.push_back(dirPath + (filename + "/"));
+				}
+				else
+				{
+					files.push_back(dirPath + filename);
+				}
+			}
+		}
+		closedir(dp);
+	}
+
+	std::time_t FileSystem::getLastModifiedTime(const Path& path)
+	{
+		struct stat st_buf;
+		stat(path.toString().c_str(), &st_buf);
+		std::time_t time = st_buf.st_mtime;
+		return time;
+	}
+
+	Path FileSystem::getWorkingDirectoryPath()
+	{
+		char *buffer = bs_newN<char>(PATH_MAX);
+		String wd;
+
+		if (getcwd(buffer, PATH_MAX) != NULL)
+			wd = buffer;
+		bs_free(buffer);
+
+		const int error = errno;
+		if (error)
+			LOGERR(String("Error when calling getcwd(): ") + strerror(error));
+
+		return Path(wd);
+	}
+
+	bool FileSystem::iterate(const Path& dirPath, std::function<bool(const Path&)> fileCallback,
+		std::function<bool(const Path&)> dirCallback, bool recursive)
+	{
+		BS_ASSERT(!"TODO: implement FileSystem::iterate()");
+		return true;
+	}
+
+	Path FileSystem::getTempDirectoryPath()
+	{
+		String tmpdir;
+
+		// Try different things:
+		// 1) If defined, honor the TMPDIR environnement variable
+		char *TMPDIR = getenv("TMPDIR");
+		if (TMPDIR != NULL)
+				tmpdir = TMPDIR;
+		else
+		{
+		// 2) If defined, honor the P_tmpdir macro
+#ifdef P_tmpdir
+		tmpdir = String(P_tmpdir);
+#else
+		// 3) If everything else fails, simply default to /tmp
+		tmpdir = String("/tmp");
+#endif
+		}
+
+		tmpdir.append("/banshee-XXXXXX");
+		size_t len = tmpdir.size()+1;
+		char *nameTemplate = bs_newN<char>(len);
+		snprintf(nameTemplate, len, "%s", tmpdir.c_str());
+
+		char *directoryName = mkdtemp(nameTemplate);
+		if (directoryName == NULL)
+		{
+			LOGERR(String(__FUNCTION__) + ": " + strerror(errno));
+			return Path(StringUtil::BLANK);
+		}
+
+		return Path(String(directoryName) + "/");
+	}
+}

+ 14 - 116
Source/BansheeUtility/Source/Win32/BsWin32FileSystem.cpp

@@ -212,28 +212,31 @@ namespace BansheeEngine
 		return true;
 	}
 
-	void win32_remove(const WString& path)
+	void FileSystem::removeFile(const Path& path)
 	{
-		if (win32_isDirectory(path))
+		WString pathStr = path.toWString();
+		if (win32_isDirectory(pathStr))
 		{
-			if (RemoveDirectoryW(path.c_str()) == 0)
-				win32_handleError(GetLastError(), path);
+			if (RemoveDirectoryW(pathStr.c_str()) == 0)
+				win32_handleError(GetLastError(), pathStr);
 		}
 		else
 		{
-			if (DeleteFileW(path.c_str()) == 0)
-				win32_handleError(GetLastError(), path);
+			if (DeleteFileW(pathStr.c_str()) == 0)
+				win32_handleError(GetLastError(), pathStr);
 		}
 	}
 
-	void win32_copyFile(const WString& from, const WString& to)
+	void FileSystem::copyFile(const Path& from, const Path& to)
 	{
-		if (CopyFileW(from.c_str(), to.c_str(), FALSE) == FALSE)
+		if (CopyFileW(from.toWString().c_str(), to.toWString().c_str(), FALSE) == FALSE)
 			win32_handleError(GetLastError(), from);
 	}
 
-	void win32_rename(const WString& oldPath, const WString& newPath)
+	void FileSystem::moveFile(const Path& oldPath, const Path& newPath)
 	{
+		WString oldPathStr = oldPath.toWString();
+		WString newPathStr = newPath.toWString();
 		if (MoveFileW(oldPath.c_str(), newPath.c_str()) == 0)
 			win32_handleError(GetLastError(), oldPath);
 	}
@@ -291,30 +294,6 @@ namespace BansheeEngine
 		return win32_getFileSize(fullPath.toWString());
 	}
 
-	void FileSystem::remove(const Path& fullPath, bool recursively)
-	{
-		WString fullPathStr = fullPath.toWString();
-
-		if (!FileSystem::exists(fullPath))
-			return;
-
-		if (recursively)
-		{
-			Vector<Path> files;
-			Vector<Path> directories;
-
-			getChildren(fullPath, files, directories);
-
-			for (auto& file : files)
-				remove(file, false);
-
-			for (auto& dir : directories)
-				remove(dir, true);
-		}
-
-		win32_remove(fullPathStr);
-	}
-
 	void FileSystem::move(const Path& oldPath, const Path& newPath, bool overwriteExisting)
 	{
 		WString oldPathStr = oldPath.toWString();
@@ -323,7 +302,7 @@ namespace BansheeEngine
 		if (win32_pathExists(newPathStr))
 		{
 			if (overwriteExisting)
-				win32_remove(newPathStr);
+				FileSystem::removeFile(newPath);
 			else
 			{
 				LOGWRN("Move operation failed because another file already exists at the new path: \"" + toString(newPathStr) + "\"");
@@ -334,87 +313,6 @@ namespace BansheeEngine
 		win32_rename(oldPathStr, newPathStr);
 	}
 
-	void FileSystem::copy(const Path& oldPath, const Path& newPath, bool overwriteExisting)
-	{
-		Stack<std::tuple<Path, Path>> todo;
-		todo.push(std::make_tuple(oldPath, newPath));
-
-		while (!todo.empty())
-		{
-			auto current = todo.top();
-			todo.pop();
-
-			Path sourcePath = std::get<0>(current);
-			WString sourcePathStr = sourcePath.toWString();
-			if (!win32_pathExists(sourcePathStr))
-				continue;
-
-			bool srcIsFile = win32_isFile(sourcePathStr);
-
-			Path destinationPath = std::get<1>(current);
-			WString destPathStr = destinationPath.toWString();
-
-			bool destExists = win32_pathExists(destPathStr);
-			if (destExists)
-			{
-				if (win32_isFile(destPathStr))
-				{
-					if (overwriteExisting)
-						win32_remove(destPathStr);
-					else
-					{
-						LOGWRN("Copy operation failed because another file already exists at the new path: \"" + toString(destPathStr) + "\"");
-						return;
-					}
-				}
-			}
-
-			bool destIsFile = !destinationPath.getWExtension().empty();
-
-			if (!srcIsFile && destIsFile)
-			{
-				LOGWRN("Cannot copy a source folder to a destination file.");
-				return;
-			}
-			else if (srcIsFile && !destIsFile)
-			{
-				Path destinationFilePath = destinationPath;
-				destinationFilePath.append(sourcePath.getWTail());
-
-				win32_copyFile(sourcePathStr, destinationFilePath.toWString());
-			}
-			else if (srcIsFile && destIsFile)
-			{
-				win32_copyFile(sourcePathStr, destPathStr);
-			}
-			else if (!srcIsFile && !destIsFile)
-			{
-				if (!destExists)
-					win32_createDirectory(destPathStr);
-
-				Vector<Path> files;
-				Vector<Path> directories;
-				getChildren(destinationPath, files, directories);
-
-				for (auto& file : files)
-				{
-					Path fileDestPath = destinationPath;
-					fileDestPath.append(file.getWTail());
-
-					todo.push(std::make_tuple(file, fileDestPath));
-				}
-
-				for (auto& dir : directories)
-				{
-					Path dirDestPath = destinationPath;
-					dirDestPath.append(dir.getWTail());
-
-					todo.push(std::make_tuple(dir, dirDestPath));
-				}
-			}
-		}
-	}
-
 	bool FileSystem::exists(const Path& fullPath)
 	{
 		return win32_pathExists(fullPath.toWString());
@@ -589,4 +487,4 @@ namespace BansheeEngine
 	{
 		return Path(win32_getTempDirectory());
 	}
-}
+}