vChewing-macOS/Source/Engine/OpenVanilla/OVFileHelper.h

628 lines
21 KiB
C++

//
// OVFileHelper.h
//
// Copyright (c) 2007-2010 Lukhnos D. Liu
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
//
#ifndef OVFileHelper_h
#define OVFileHelper_h
#if defined(__APPLE__)
#include <dirent.h>
#include <stdio.h>
#else
#include <windows.h>
#include <shlobj.h>
#include <stdio.h>
#endif
#include <sys/stat.h>
#include <fstream>
#include <algorithm>
#include <vector>
#include "OVWildcard.h"
#include "OVUTF8Helper.h"
namespace OpenVanilla {
using namespace std;
class OVFileHelper {
public:
static FILE* OpenStream(const string& filename, const string& mode = "rb")
{
#ifndef WIN32
return fopen(filename.c_str(), mode.c_str());
#else
FILE* stream = NULL;
errno_t err = _wfopen_s(&stream, OVUTF16::FromUTF8(filename).c_str(), OVUTF16::FromUTF8(mode).c_str());
return err ? 0 : stream;
#endif
}
static void OpenOFStream(ofstream& stream, const string& filename, ios_base::openmode openMode)
{
#ifndef WIN32
stream.open(filename.c_str(), openMode);
#else
stream.open(OVUTF16::FromUTF8(filename).c_str(), openMode);
#endif
}
static void OpenIFStream(ifstream& stream, const string& filename, ios_base::openmode openMode)
{
#ifndef WIN32
stream.open(filename.c_str(), openMode);
#else
stream.open(OVUTF16::FromUTF8(filename).c_str(), openMode);
#endif
}
// use free(), not delete[], to free the block allocated
static pair<char*, size_t> SlurpFile(const string& filename, bool addOneByteOfStringTerminationPadding = false)
{
FILE* stream = OpenStream(filename);
char* buf = 0;
size_t size = 0;
if (stream) {
if (!fseek(stream, 0, SEEK_END)) {
long lsize = ftell(stream);
if (lsize) {
size = (size_t)lsize;
if (!fseek(stream, 0, SEEK_SET)) {
buf = (char*)calloc(1, (size_t)size + (addOneByteOfStringTerminationPadding ? 1 : 0));
if (buf) {
if (fread(buf, size, 1, stream) != 1) {
free(buf);
buf = 0;
size = 0;
}
}
else {
size = 0;
}
}
else {
size = 0;
}
}
}
fclose(stream);
}
return pair<char*, size_t>(buf, size);
}
};
class OVFileTimestamp {
public:
#if defined(__APPLE__)
OVFileTimestamp(__darwin_time_t timestamp = 0, long subtimestamp = 0)
#elif defined(WIN32)
OVFileTimestamp(time_t timestamp = 0, time_t subtimestamp = 0)
#else
#error We don't know about Linux yet, sorry.
#endif
: m_timestamp(timestamp)
, m_subtimestamp(subtimestamp)
{
}
OVFileTimestamp(const OVFileTimestamp& timestamp)
: m_timestamp(timestamp.m_timestamp)
, m_subtimestamp(timestamp.m_subtimestamp)
{
}
OVFileTimestamp& operator=(const OVFileTimestamp& timestamp)
{
m_timestamp = timestamp.m_timestamp;
m_subtimestamp = timestamp.m_subtimestamp;
return *this;
}
bool operator==(OVFileTimestamp& another)
{
return (m_timestamp == another.m_timestamp) && (m_subtimestamp == another.m_subtimestamp);
}
bool operator!=(OVFileTimestamp& another)
{
return (m_timestamp != another.m_timestamp) || (m_subtimestamp != another.m_subtimestamp);
}
bool operator<(OVFileTimestamp& another)
{
return (m_timestamp < another.m_timestamp) || ((m_timestamp == another.m_timestamp) && m_subtimestamp < another.m_subtimestamp);
}
bool operator>(OVFileTimestamp& another)
{
return (m_timestamp > another.m_timestamp) || ((m_timestamp == another.m_timestamp) && m_subtimestamp > another.m_subtimestamp);
}
protected:
#if defined(__APPLE__)
__darwin_time_t m_timestamp;
long m_subtimestamp;
#elif defined(WIN32)
time_t m_timestamp;
time_t m_subtimestamp;
#else
#error We don't know about Linux yet, sorry.
#endif
};
class OVPathHelper {
public:
static char Separator()
{
#ifndef WIN32
return '/';
#else
return '\\';
#endif
}
static const string DirectoryFromPath(const string& path)
{
string realPath = OVPathHelper::NormalizeByExpandingTilde(path);
if (OVPathHelper::PathExists(realPath) && OVPathHelper::IsDirectory(realPath))
return realPath;
char separator = OVPathHelper::Separator();
if (!realPath.length())
return string(".");
for (size_t index = realPath.length() - 1; index >= 0 ; index--) {
if (realPath[index] == separator) {
if (index) {
// reserve the \\ on Windows
if (realPath[index-1] == '\\')
return realPath.substr(0, index + 1);
else
return realPath.substr(0, index);
}
else
return realPath.substr(0, 1);
}
// if we run into : (like C:) on Windows
if (realPath[index] == ':')
return realPath.substr(0, index + 1) + Separator();
if (!index) break;
}
return string(".");
}
static const string FilenameWithoutPath(const string &path)
{
char separator = OVPathHelper::Separator();
if (!path.length())
return string();
for (size_t index = path.length() - 1; index >= 0 ; index--) {
if (path[index] == separator)
return path.substr(index + 1, path.length() - (index + 1));
if (!index) break;
}
return path;
}
static const string FilenameWithoutExtension(const string &path)
{
char separator = OVPathHelper::Separator();
if (!path.length())
return string();
for (size_t index = path.length() - 1; index >= 0 ; index--) {
if (path[index] == separator)
break;
if (path[index] == '.')
return path.substr(0, index);
if (!index) break;
}
return path;
}
static const string ChopTrailingSeparator(const string& path)
{
if (path.length() == 1 && path[0] == Separator())
return path;
string result;
if (path.length()) {
if (path[path.length() - 1] == Separator())
result = path.substr(0, path.length() - 1);
else
result = path;
}
return result;
}
static const string ChopLeadingSeparator(const string& path)
{
if (path.length() == 1 && path[0] == Separator())
return path;
string result;
if (path.length()) {
if (path[0] == Separator()) {
result = path.substr(1, path.length() - 1);
}
else {
result = path;
}
}
return result;
}
static const string PathCat(const string& s1, const string& s2)
{
if (s2.length())
return ChopTrailingSeparator(s1) + Separator() + ChopLeadingSeparator(s2);
else
return s1;
}
static const string Normalize(const string& path)
{
string newPath;
size_t length = path.length();
for (size_t index = 0; index < length; index++) {
if (path[index] == '/' || path[index] == '\\') {
if (index < length - 1) {
if (path[index+1] == '/' || path[index+1] == '\\')
index++;
}
newPath += Separator();
}
else
newPath += path[index];
}
return ChopTrailingSeparator(newPath);
}
static const bool PathExists(const string& path)
{
#ifndef WIN32
struct stat buf;
return !stat(path.c_str(), &buf);
#else
struct _stat buf;
wstring wpath = OVUTF16::FromUTF8(path);
return !_wstat(wpath.c_str(), &buf);
#endif
}
static const bool IsDirectory(const string& path)
{
#ifndef WIN32
struct stat buf;
if (!stat(path.c_str(), &buf))
{
if (buf.st_mode & S_IFDIR)
return true;
}
#else
struct _stat buf;
wstring wpath = OVUTF16::FromUTF8(path);
if (!_wstat(wpath.c_str(), &buf))
{
if (buf.st_mode & S_IFDIR)
return true;
}
#endif
return false;
}
static const OVFileTimestamp TimestampForPath(const string& path)
{
OVFileTimestamp timestamp;
#if defined(__APPLE__)
struct stat buf;
if (!stat(path.c_str(), &buf))
{
timestamp = OVFileTimestamp(buf.st_mtimespec.tv_sec, buf.st_mtimespec.tv_nsec);
}
#elif defined(WIN32)
struct _stat buf;
wstring wpath = OVUTF16::FromUTF8(path);
if (!_wstat(wpath.c_str(), &buf))
{
timestamp = OVFileTimestamp(buf.st_mtime);
}
#else
#error Sorry, no idea for Linux yet.
#endif
return timestamp;
}
static bool RemoveEverythingAtPath(const string& path);
static const string NormalizeByExpandingTilde(const string& path);
};
class OVDirectoryHelper {
public:
static bool MakeDirectory(const string& path)
{
#ifndef WIN32
return !mkdir(path.c_str(), S_IRWXU);
#else
wstring wpath = OVUTF16::FromUTF8(path);
return CreateDirectoryW(wpath.c_str(), NULL) == TRUE;
#endif
}
static bool MakeDirectoryWithImmediates(const string& path)
{
string realPath = OVPathHelper::NormalizeByExpandingTilde(path);
if (OVPathHelper::PathExists(realPath) && OVPathHelper::IsDirectory(realPath)) {
return true;
}
string lastPart = OVPathHelper::DirectoryFromPath(realPath);
if (lastPart != realPath && !OVPathHelper::PathExists(lastPart)) {
if (!MakeDirectoryWithImmediates(lastPart))
return false;
}
return MakeDirectory(realPath);
}
static bool CheckDirectory(const string& path)
{
string realPath = OVPathHelper::NormalizeByExpandingTilde(path);
if (OVPathHelper::PathExists(realPath))
return OVPathHelper::IsDirectory(realPath);
return MakeDirectoryWithImmediates(realPath);
}
static const vector<string> Glob(const string& directory, const string& pattern = "*", const string& excludePattern = "", size_t depth = 1)
{
string sanitizedDirectory = OVPathHelper::NormalizeByExpandingTilde(directory);
OVWildcard expression(pattern);
OVWildcard negativeExpression(excludePattern);
vector<string> dirResult;
vector<string> fileResult;
struct dirent** namelist = NULL;
#ifndef WIN32
int count = scandir(sanitizedDirectory.c_str(), &namelist, NULL, NULL);
for (int index = 0; index < count; index++) {
struct dirent* entry = namelist[index];
bool isDir = entry->d_type == DT_DIR;
string name = entry->d_name;
#else
vector<pair<string, bool> > foundFiles;
WIN32_FIND_DATAW findData;
HANDLE findHandle = FindFirstFileW(OVUTF16::FromUTF8(OVPathHelper::PathCat(sanitizedDirectory, "*.*")).c_str(), &findData);
if (findHandle != INVALID_HANDLE_VALUE) {
foundFiles.push_back(pair<string, bool>(OVUTF8::FromUTF16(findData.cFileName), (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0));
while (FindNextFileW(findHandle, &findData))
{
foundFiles.push_back(pair<string, bool>(OVUTF8::FromUTF16(findData.cFileName), (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0));
}
FindClose(findHandle);
}
size_t count = foundFiles.size();
for (size_t index = 0; index < count; index++) {
string name = foundFiles[index].first;
bool isDir = foundFiles[index].second;
#endif
if ((!depth || depth > 1) && isDir && name != "." && name != "..") {
vector<string> subResult = Glob(OVPathHelper::PathCat(sanitizedDirectory, name), pattern, excludePattern, depth ? depth - 1 : 0);
vector<string>::iterator iter = subResult.begin();
for ( ; iter != subResult.end() ; iter++)
dirResult.push_back(*iter);
}
if (name != "." && name != ".." && expression.match(name) && !negativeExpression.match(name)) {
fileResult.push_back(OVPathHelper::PathCat(sanitizedDirectory, name));
}
#ifndef WIN32
free(entry);
#endif
}
#ifndef WIN32
if (count != -1)
free(namelist);
#endif
sort(dirResult.begin(), dirResult.end());
sort(fileResult.begin(), fileResult.end());
for (vector<string>::iterator iter = dirResult.begin() ; iter != dirResult.end(); iter++)
fileResult.push_back(*iter);
return fileResult;
}
static const string TempDirectory()
{
#ifndef WIN32
return string("/tmp");
#else
WCHAR path[MAX_PATH + 1];
GetTempPathW(MAX_PATH, path);
return OVUTF8::FromUTF16(path);
#endif
}
static const string GenerateTempFilename(const string& prefix = "temp")
{
string pattern = OVPathHelper::PathCat(TempDirectory(), prefix + "-" + "XXXXXXXX");
#ifndef WIN32
char *p = (char*)calloc(1, pattern.length() + 1);
strcpy(p, pattern.c_str());
char *r = mktemp(p);
if (!r) {
free(p);
return string();
}
string result = r;
free(p);
return result;
#else
wstring wp = OVUTF16::FromUTF8(pattern);
size_t length = wp.length() + 1;
wchar_t* p = (wchar_t*)calloc(1, length * sizeof(wchar_t));
wcscpy_s(p, length, wp.c_str());
errno_t err = _wmktemp_s(p, length);
if (err != 0) {
free(p);
return string();
}
string result = OVUTF8::FromUTF16(p);
free(p);
return result;
#endif
}
static const string UserHomeDirectory()
{
#ifndef WIN32
const char* homePath = getenv("HOME");
if (homePath)
return string(homePath);
return TempDirectory();
#else
WCHAR path[MAX_PATH + 1];
HRESULT error = SHGetFolderPathW(NULL, CSIDL_PROFILE, NULL, SHGFP_TYPE_CURRENT, path);
if (!error) {
return OVUTF8::FromUTF16(path);
}
return TempDirectory();
#endif
}
static const string UserApplicationDataDirectory(const string& applicationName)
{
#if defined(__APPLE__)
return OVPathHelper::PathCat(UserHomeDirectory(), OVPathHelper::PathCat("/Library/", applicationName));
#elif defined(WIN32)
WCHAR path[MAX_PATH + 1];
HRESULT error = SHGetFolderPathW(NULL, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT, path);
if (!error) {
return OVPathHelper::PathCat(OVUTF8::FromUTF16(path), applicationName);
}
return OVPathHelper::PathCat(TempDirectory(), applicationName);
#else
return OVPathHelper::PathCat(UserHomeDirectory(), string(".") + applicationName);
#endif
}
static const string UserApplicationSupportDataDirectory(const string& applicationName)
{
#ifdef __APPLE__
return OVPathHelper::PathCat(UserHomeDirectory(), OVPathHelper::PathCat("/Library/Application Support", applicationName));
#else
return UserApplicationDataDirectory(applicationName);
#endif
}
static const string UserPreferencesDirectory(const string& applicationName)
{
#ifdef __APPLE__
return OVPathHelper::NormalizeByExpandingTilde("~/Library/Preferences");
#else
return UserApplicationDataDirectory(applicationName);
#endif
}
};
inline const string OVPathHelper::NormalizeByExpandingTilde(const string& path)
{
string newPath = Normalize(path);
if (newPath.length()) {
if (newPath[0] == '~') {
newPath = OVPathHelper::PathCat(OVDirectoryHelper::UserHomeDirectory(), newPath.substr(1, newPath.length()));
}
}
return newPath;
}
inline bool OVPathHelper::RemoveEverythingAtPath(const string& path)
{
if (!PathExists(path)) return false;
if (IsDirectory(path)) {
vector<string> files = OVDirectoryHelper::Glob(path, "*", "", 0);
vector<string>::iterator iter = files.begin();
for ( ; iter != files.end(); iter++) {
if (!RemoveEverythingAtPath(*iter))
return false;
}
#ifndef WIN32
return !rmdir(path.c_str());
#else
wstring wpath = OVUTF16::FromUTF8(path);
return RemoveDirectoryW(wpath.c_str()) == TRUE;
#endif
}
else {
#ifndef WIN32
return !unlink(path.c_str());
#else
wstring wpath = OVUTF16::FromUTF8(path);
return DeleteFileW(wpath.c_str()) == TRUE;
#endif
}
}
};
#endif