533 lines
10 KiB
C++
533 lines
10 KiB
C++
// Copyright (C) 2015 Jérôme Leclercq
|
|
// This file is part of the "Nazara Engine - Core module"
|
|
// For conditions of distribution and use, see copyright notice in Config.hpp
|
|
|
|
#include <Nazara/Core/Directory.hpp>
|
|
#include <Nazara/Core/Config.hpp>
|
|
#include <Nazara/Core/Error.hpp>
|
|
#include <Nazara/Core/File.hpp>
|
|
#include <cstddef>
|
|
#include <cstring>
|
|
#include <string>
|
|
|
|
#if defined(NAZARA_PLATFORM_WINDOWS)
|
|
#include <Nazara/Core/Win32/DirectoryImpl.hpp>
|
|
#elif defined(NAZARA_PLATFORM_POSIX)
|
|
#include <Nazara/Core/Posix/DirectoryImpl.hpp>
|
|
#else
|
|
#error OS not handled
|
|
#endif
|
|
|
|
#if NAZARA_CORE_THREADSAFE && NAZARA_THREADSAFETY_DIRECTORY
|
|
#include <Nazara/Core/ThreadSafety.hpp>
|
|
#else
|
|
#include <Nazara/Core/ThreadSafetyOff.hpp>
|
|
#endif
|
|
|
|
#include <Nazara/Core/Debug.hpp>
|
|
|
|
namespace Nz
|
|
{
|
|
namespace
|
|
{
|
|
//FIXME: MinGW seems to dislike thread_local shared_ptr.. (using a std::string is a working hackfix)
|
|
thread_local std::string currentPath(DirectoryImpl::GetCurrent());
|
|
}
|
|
|
|
/*!
|
|
* \ingroup core
|
|
* \class Nz::Directory
|
|
* \brief Core class that represents a directory
|
|
*/
|
|
|
|
/*!
|
|
* \brief Constructs a Directory object by default
|
|
*/
|
|
|
|
Directory::Directory() :
|
|
m_pattern('*'),
|
|
m_impl(nullptr)
|
|
{
|
|
}
|
|
|
|
/*!
|
|
* \brief Constructs a Directory object with a path
|
|
*
|
|
* \param dirPath Path to the directory
|
|
*/
|
|
|
|
Directory::Directory(const String& dirPath) :
|
|
m_dirPath(dirPath),
|
|
m_pattern('*'),
|
|
m_impl(nullptr)
|
|
{
|
|
}
|
|
|
|
/*!
|
|
* \brief Destructs the object and calls Close
|
|
*
|
|
* \see Close
|
|
*/
|
|
|
|
Directory::~Directory()
|
|
{
|
|
Close();
|
|
}
|
|
|
|
/*!
|
|
* \brief Closes the directory
|
|
*/
|
|
|
|
void Directory::Close()
|
|
{
|
|
NazaraLock(m_mutex);
|
|
|
|
if (m_impl)
|
|
{
|
|
m_impl->Close();
|
|
delete m_impl;
|
|
m_impl = nullptr;
|
|
}
|
|
}
|
|
|
|
/*!
|
|
* \brief Checks whether the directory exists
|
|
* \return true if directory exists
|
|
*/
|
|
|
|
bool Directory::Exists() const
|
|
{
|
|
NazaraLock(m_mutex);
|
|
|
|
if (IsOpen())
|
|
return true; // If directory is open, then it exists
|
|
else
|
|
return Exists(m_dirPath);
|
|
}
|
|
|
|
/*!
|
|
* \brief Gets the path of the directory
|
|
* \return Path of the directory
|
|
*/
|
|
|
|
String Directory::GetPath() const
|
|
{
|
|
NazaraLock(m_mutex);
|
|
|
|
return m_dirPath;
|
|
}
|
|
|
|
/*!
|
|
* \brief Gets the pattern for the path of the directory
|
|
* \return Pattern for the path of the directory
|
|
*/
|
|
|
|
String Directory::GetPattern() const
|
|
{
|
|
NazaraLock(m_mutex);
|
|
|
|
return m_pattern;
|
|
}
|
|
|
|
/*!
|
|
* \brief Gets the result name of the directory
|
|
* \return Resulting name
|
|
*
|
|
* \remark Produces a NazaraError if directory is not open with NAZARA_CORE_SAFE defined
|
|
*/
|
|
|
|
String Directory::GetResultName() const
|
|
{
|
|
NazaraLock(m_mutex);
|
|
|
|
#if NAZARA_CORE_SAFE
|
|
if (!IsOpen())
|
|
{
|
|
NazaraError("Directory not opened");
|
|
return String();
|
|
}
|
|
#endif
|
|
|
|
return m_impl->GetResultName();
|
|
}
|
|
|
|
/*!
|
|
* \brief Gets the result path of the directory
|
|
* \return Resulting path
|
|
*
|
|
* \remark Produces a NazaraError if directory is not open with NAZARA_CORE_SAFE defined
|
|
*/
|
|
|
|
String Directory::GetResultPath() const
|
|
{
|
|
NazaraLock(m_mutex);
|
|
|
|
#if NAZARA_CORE_SAFE
|
|
if (!IsOpen())
|
|
{
|
|
NazaraError("Directory not opened");
|
|
return String();
|
|
}
|
|
#endif
|
|
|
|
return m_dirPath + NAZARA_DIRECTORY_SEPARATOR + m_impl->GetResultName();
|
|
}
|
|
|
|
/*!
|
|
* \brief Gets the resulting size of the directory
|
|
* \return Size of the directory
|
|
*
|
|
* \remark Produces a NazaraError if directory is not open with NAZARA_CORE_SAFE defined
|
|
*/
|
|
|
|
UInt64 Directory::GetResultSize() const
|
|
{
|
|
NazaraLock(m_mutex);
|
|
|
|
#if NAZARA_CORE_SAFE
|
|
if (!IsOpen())
|
|
{
|
|
NazaraError("Directory not opened");
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
return m_impl->GetResultSize();
|
|
}
|
|
|
|
/*!
|
|
* \brief Checks whether the directory is open
|
|
* \return true if open
|
|
*/
|
|
|
|
bool Directory::IsOpen() const
|
|
{
|
|
NazaraLock(m_mutex);
|
|
|
|
return m_impl != nullptr;
|
|
}
|
|
|
|
/*!
|
|
* \brief Checks whether the directory is result
|
|
* \return true if result
|
|
*
|
|
* \remark Produces a NazaraError if directory is not open with NAZARA_CORE_SAFE defined
|
|
*/
|
|
|
|
bool Directory::IsResultDirectory() const
|
|
{
|
|
NazaraLock(m_mutex);
|
|
|
|
#if NAZARA_CORE_SAFE
|
|
if (!IsOpen())
|
|
{
|
|
NazaraError("Directory not opened");
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
return m_impl->IsResultDirectory();
|
|
}
|
|
|
|
/*!
|
|
* \brief Sets the next result in the directory
|
|
* \return true if directory has a next result
|
|
*
|
|
* \param skipDots Skips the dots in the path
|
|
*
|
|
* \remark Produces a NazaraError if directory is not open with NAZARA_CORE_SAFE defined
|
|
*/
|
|
|
|
bool Directory::NextResult(bool skipDots)
|
|
{
|
|
NazaraLock(m_mutex);
|
|
|
|
#if NAZARA_CORE_SAFE
|
|
if (!IsOpen())
|
|
{
|
|
NazaraError("Directory not opened");
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
String name;
|
|
for (;;)
|
|
{
|
|
if (!m_impl->NextResult())
|
|
return false;
|
|
|
|
name = m_impl->GetResultName();
|
|
|
|
if (skipDots && (name == '.' || name == ".."))
|
|
continue;
|
|
|
|
if (name.Match(m_pattern))
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*!
|
|
* \brief Opens the directory
|
|
* \return true if opening is successful
|
|
*/
|
|
|
|
bool Directory::Open()
|
|
{
|
|
NazaraLock(m_mutex);
|
|
|
|
Close();
|
|
|
|
if (!Exists(m_dirPath))
|
|
return false;
|
|
|
|
m_impl = new DirectoryImpl(this);
|
|
if (!m_impl->Open(m_dirPath))
|
|
{
|
|
delete m_impl;
|
|
m_impl = nullptr;
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*!
|
|
* \brief Sets the path of the directory
|
|
*
|
|
* \param dirPath Path of the directory
|
|
*/
|
|
|
|
void Directory::SetPath(const String& dirPath)
|
|
{
|
|
NazaraLock(m_mutex);
|
|
|
|
Close();
|
|
|
|
m_dirPath = File::AbsolutePath(dirPath);
|
|
}
|
|
|
|
/*!
|
|
* \brief Sets the pattern of the directory
|
|
*
|
|
* \param dirPath Pattern of the directory
|
|
*/
|
|
|
|
void Directory::SetPattern(const String& pattern)
|
|
{
|
|
NazaraLock(m_mutex);
|
|
|
|
m_pattern = pattern;
|
|
}
|
|
|
|
/*!
|
|
* \brief Copies the first directory to a new directory path
|
|
* \return true if copy is successful
|
|
*
|
|
* \param sourcePath Path of the original directory
|
|
* \param targetPath Path of the copied directory
|
|
*
|
|
* \remark Produces a NazaraError if could not create destination directory
|
|
* \remark Produces a NazaraError if could not open origin directory
|
|
* \remark Produces a NazaraError if could not copy a file
|
|
*/
|
|
|
|
bool Directory::Copy(const String& sourcePath, const String& destPath)
|
|
{
|
|
if (sourcePath.IsEmpty() || destPath.IsEmpty())
|
|
return false;
|
|
|
|
String dirPath(sourcePath);
|
|
String dest(File::NormalizePath(destPath));
|
|
|
|
if (!Create(destPath, true))
|
|
{
|
|
NazaraError("Unable to create \"" + destPath + '"');
|
|
return false;
|
|
}
|
|
|
|
Directory dir(dirPath);
|
|
if (!dir.Open())
|
|
{
|
|
NazaraError("Unable to open \"" + destPath + '"');
|
|
return false;
|
|
}
|
|
|
|
while (dir.NextResult(true))
|
|
{
|
|
if (dir.IsResultDirectory())
|
|
{
|
|
if (!Copy(dir.GetResultPath(), dest + NAZARA_DIRECTORY_SEPARATOR + dir.GetResultName()))
|
|
return false;
|
|
}
|
|
else if (!File::Copy(dir.GetResultPath(), dest + NAZARA_DIRECTORY_SEPARATOR + dir.GetResultName()))
|
|
{
|
|
NazaraError("Failed to copy \"" + dir.GetResultPath() + "\" to \"" + dest + NAZARA_DIRECTORY_SEPARATOR + dir.GetResultName() + '"');
|
|
return false;
|
|
}
|
|
}
|
|
|
|
dir.Close();
|
|
|
|
return true;
|
|
}
|
|
|
|
/*!
|
|
* \brief Creates a directory from a path
|
|
* \return true if creation is successful
|
|
*
|
|
* \param dirPath Path of the directory
|
|
* \param recursive Creates subdirectories
|
|
*/
|
|
|
|
bool Directory::Create(const String& dirPath, bool recursive)
|
|
{
|
|
if (dirPath.IsEmpty())
|
|
return false;
|
|
|
|
if (recursive)
|
|
{
|
|
String path = File::NormalizePath(dirPath);
|
|
std::size_t foundPos = path.Find(NAZARA_DIRECTORY_SEPARATOR);
|
|
if (foundPos == String::npos)
|
|
return false;
|
|
|
|
#ifdef NAZARA_PLATFORM_WINDOWS
|
|
// Unlike to disk (Ex: "C:"), the netwrok path is not considered as a directory (Ex: "\\Laptop")
|
|
if (path.Match("\\\\*"))
|
|
{
|
|
foundPos = path.Find('\\', 2);
|
|
if (foundPos == String::npos)
|
|
return false;
|
|
|
|
foundPos = path.Find('\\', foundPos + 1);
|
|
if (foundPos == String::npos)
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
for (;;)
|
|
{
|
|
String p = path.SubString(0, foundPos);
|
|
if (p.EndsWith(NAZARA_DIRECTORY_SEPARATOR))
|
|
p = p.SubString(0, -2);
|
|
|
|
if (!DirectoryImpl::Exists(p) && !DirectoryImpl::Create(p))
|
|
return false;
|
|
|
|
if (foundPos == String::npos)
|
|
break;
|
|
|
|
foundPos = path.Find(NAZARA_DIRECTORY_SEPARATOR, foundPos + 1);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
else
|
|
return DirectoryImpl::Create(File::NormalizePath(dirPath));
|
|
}
|
|
|
|
/*!
|
|
* \brief Checks whether the directory exists
|
|
* \return true if directory exists
|
|
*
|
|
* \param dirPath Path of the directory
|
|
*/
|
|
|
|
bool Directory::Exists(const String& dirPath)
|
|
{
|
|
if (dirPath.IsEmpty())
|
|
return false;
|
|
|
|
return DirectoryImpl::Exists(File::NormalizePath(dirPath));
|
|
}
|
|
|
|
/*!
|
|
* \brief Gets the current path of this directory
|
|
* \return Current path
|
|
*/
|
|
|
|
String Directory::GetCurrent()
|
|
{
|
|
return currentPath;
|
|
}
|
|
|
|
/*!
|
|
* \brief Gets this current file relative to the engine
|
|
* \return Path to this file
|
|
*/
|
|
|
|
const char* Directory::GetCurrentFileRelativeToEngine(const char* currentFile)
|
|
{
|
|
///FIXME: Is this method in the right place ?
|
|
const char* ptr = std::strstr(currentFile, "NazaraEngine/");
|
|
if (!ptr)
|
|
ptr = std::strstr(currentFile, "NazaraEngine\\");
|
|
|
|
if (!ptr)
|
|
ptr = currentFile;
|
|
|
|
return ptr;
|
|
}
|
|
|
|
/*!
|
|
* \brief Removes the directory
|
|
* \return true if remove is successful
|
|
*
|
|
* \param dirPath Path of the directory
|
|
* \param emptyDirectory Remove recursively
|
|
*/
|
|
|
|
bool Directory::Remove(const String& dirPath, bool emptyDirectory)
|
|
{
|
|
if (dirPath.IsEmpty())
|
|
return false;
|
|
|
|
if (emptyDirectory)
|
|
{
|
|
Directory dir(dirPath);
|
|
if (!dir.Open())
|
|
return DirectoryImpl::Remove(dirPath); // If we can't open the directory, we try to delete it
|
|
|
|
while (dir.NextResult(true))
|
|
{
|
|
if (dir.IsResultDirectory())
|
|
{
|
|
if (!Remove(dir.GetResultPath(), true))
|
|
return false;
|
|
}
|
|
else if (!File::Delete(dir.GetResultPath()))
|
|
{
|
|
NazaraError(dir.GetResultPath());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
dir.Close();
|
|
}
|
|
|
|
return DirectoryImpl::Remove(File::NormalizePath(dirPath));
|
|
}
|
|
|
|
/*!
|
|
* \brief Sets the current directory
|
|
* \return true if directory path exists
|
|
*
|
|
* \param dirPath Path of the directory
|
|
*/
|
|
|
|
bool Directory::SetCurrent(const String& dirPath)
|
|
{
|
|
String path = File::AbsolutePath(dirPath);
|
|
if (DirectoryImpl::Exists(path))
|
|
{
|
|
currentPath = path;
|
|
return true;
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
}
|