Files
nzbget/daemon/util/FileSystem.cpp
Andrey Prygunkov 1e59b9976a #136, #127: forcing extended paths with unrar (Windows)
Instead of using extended (extra long) path notation only if the path
is really long, we now always use extended notation since we don’t know
how long the paths inside archive are.
2016-03-22 18:25:42 +01:00

1100 lines
22 KiB
C++

/*
* This file is part of nzbget. See <http://nzbget.net>.
*
* Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "nzbget.h"
#include "FileSystem.h"
#include "Util.h"
CString FileSystem::GetLastErrorMessage()
{
BString<1024> msg;
strerror_r(errno, msg, msg.Capacity());
return *msg;
}
void FileSystem::NormalizePathSeparators(char* path)
{
for (char* p = path; *p; p++)
{
if (*p == ALT_PATH_SEPARATOR)
{
*p = PATH_SEPARATOR;
}
}
}
#ifdef WIN32
bool FileSystem::ForceDirectories(const char* path, CString& errmsg)
{
errmsg.Clear();
BString<1024> normPath = path;
NormalizePathSeparators(normPath);
int len = strlen(normPath);
if (len > 3 && normPath[len - 1] == PATH_SEPARATOR)
{
normPath[len - 1] = '\0';
}
if (DirectoryExists(normPath))
{
return true;
}
if (FileExists(normPath))
{
errmsg.Format("path %s is not a directory", *normPath);
return false;
}
if (strlen(normPath) > 2)
{
BString<1024> parentPath = *normPath;
char* p = (char*)strrchr(parentPath, PATH_SEPARATOR);
if (p)
{
if (p - parentPath == 2 && parentPath[1] == ':' && strlen(parentPath) > 2)
{
parentPath[3] = '\0';
}
else
{
*p = '\0';
}
if (strlen(parentPath) != strlen(path) && !ForceDirectories(parentPath, errmsg))
{
return false;
}
}
if (_wmkdir(UtfPathToWidePath(normPath)) != 0 && errno != EEXIST)
{
errmsg.Format("could not create directory %s: %s", *normPath, *GetLastErrorMessage());
return false;
}
if (DirectoryExists(normPath))
{
return true;
}
if (FileExists(normPath))
{
errmsg.Format("path %s is not a directory", *normPath);
return false;
}
}
return false;
}
#else
bool FileSystem::ForceDirectories(const char* path, CString& errmsg)
{
errmsg.Clear();
BString<1024> normPath = path;
NormalizePathSeparators(normPath);
int len = strlen(normPath);
if ((len > 0) && normPath[len - 1] == PATH_SEPARATOR)
{
normPath[len - 1] = '\0';
}
struct stat buffer;
bool ok = !stat(normPath, &buffer);
if (!ok && errno != ENOENT)
{
errmsg.Format("could not read information for directory %s: errno %i, %s",
*normPath, errno, *GetLastErrorMessage());
return false;
}
if (ok && !S_ISDIR(buffer.st_mode))
{
errmsg.Format("path %s is not a directory", *normPath);
return false;
}
if (!ok)
{
BString<1024> parentPath = *normPath;
char* p = (char*)strrchr(parentPath, PATH_SEPARATOR);
if (p)
{
*p = '\0';
if (strlen(parentPath) != strlen(path) && !ForceDirectories(parentPath, errmsg))
{
return false;
}
}
if (mkdir(normPath, S_DIRMODE) != 0 && errno != EEXIST)
{
errmsg.Format("could not create directory %s: %s", *normPath, *GetLastErrorMessage());
return false;
}
if (stat(normPath, &buffer) != 0)
{
errmsg.Format("could not read information for directory %s: %s",
*normPath, *GetLastErrorMessage());
return false;
}
if (!S_ISDIR(buffer.st_mode))
{
errmsg.Format("path %s is not a directory", *normPath);
return false;
}
}
return true;
}
#endif
CString FileSystem::GetCurrentDirectory()
{
#ifdef WIN32
wchar_t unistr[1024];
::GetCurrentDirectoryW(1024, unistr);
return WidePathToUtfPath(unistr);
#else
char str[1024];
getcwd(str, 1024);
return str;
#endif
}
bool FileSystem::SetCurrentDirectory(const char* dirFilename)
{
#ifdef WIN32
return ::SetCurrentDirectoryW(UtfPathToWidePath(dirFilename));
#else
return chdir(dirFilename) == 0;
#endif
}
bool FileSystem::DirEmpty(const char* dirFilename)
{
DirBrowser dir(dirFilename);
return dir.Next() == nullptr;
}
bool FileSystem::LoadFileIntoBuffer(const char* filename, CharBuffer& buffer, bool addTrailingNull)
{
DiskFile file;
if (!file.Open(filename, DiskFile::omRead))
{
return false;
}
// obtain file size.
file.Seek(0, DiskFile::soEnd);
int size = (int)file.Position();
file.Seek(0);
// allocate memory to contain the whole file.
buffer.Reserve(size + (addTrailingNull ? 1 : 0));
// copy the file into the buffer.
file.Read(buffer, size);
file.Close();
if (addTrailingNull)
{
buffer[size] = 0;
}
return true;
}
bool FileSystem::SaveBufferIntoFile(const char* filename, const char* buffer, int bufLen)
{
DiskFile file;
if (!file.Open(filename, DiskFile::omWrite))
{
return false;
}
int writtenBytes = (int)file.Write(buffer, bufLen);
file.Close();
return writtenBytes == bufLen;
}
bool FileSystem::CreateSparseFile(const char* filename, int64 size, CString& errmsg)
{
errmsg.Clear();
bool ok = false;
#ifdef WIN32
HANDLE hFile = CreateFileW(UtfPathToWidePath(filename), GENERIC_WRITE, FILE_SHARE_READ, 0, CREATE_NEW, 0, nullptr);
if (hFile == INVALID_HANDLE_VALUE)
{
errmsg = GetLastErrorMessage();
return false;
}
// first try to create sparse file (supported only on NTFS partitions),
// it may fail but that's OK.
DWORD dwBytesReturned;
DeviceIoControl(hFile, FSCTL_SET_SPARSE, nullptr, 0, nullptr, 0, &dwBytesReturned, nullptr);
LARGE_INTEGER size64;
size64.QuadPart = size;
SetFilePointerEx(hFile, size64, nullptr, FILE_END);
SetEndOfFile(hFile);
CloseHandle(hFile);
ok = true;
#else
// create file
FILE* file = fopen(filename, FOPEN_AB);
if (!file)
{
errmsg = GetLastErrorMessage();
return false;
}
fclose(file);
// there are no reliable function to expand file on POSIX, so we must try different approaches,
// starting with the fastest one and hoping it will work
// 1) set file size using function "truncate" (this is fast, if it works)
truncate(filename, size);
// check if it worked
ok = FileSize(filename) == size;
if (!ok)
{
// 2) truncate did not work, expanding the file by writing to it (that's slow)
truncate(filename, 0);
file = fopen(filename, FOPEN_AB);
if (!file)
{
errmsg = GetLastErrorMessage();
return false;
}
char c = '0';
fwrite(&c, 1, size, file);
fclose(file);
ok = FileSize(filename) == size;
}
#endif
return ok;
}
bool FileSystem::TruncateFile(const char* filename, int size)
{
#ifdef WIN32
FILE* file = _wfopen(UtfPathToWidePath(filename), WString(FOPEN_RBP));
fseek(file, size, SEEK_SET);
bool ok = SetEndOfFile((HANDLE)_get_osfhandle(_fileno(file))) != 0;
fclose(file);
return ok;
#else
return truncate(filename, size) == 0;
#endif
}
char* FileSystem::BaseFileName(const char* filename)
{
char* p = (char*)strrchr(filename, PATH_SEPARATOR);
char* p1 = (char*)strrchr(filename, ALT_PATH_SEPARATOR);
if (p1)
{
if ((p && p < p1) || !p)
{
p = p1;
}
}
if (p)
{
return p + 1;
}
else
{
return (char*)filename;
}
}
//replace bad chars in filename
void FileSystem::MakeValidFilename(char* filename, char cReplaceChar, bool allowSlashes)
{
const char* replaceChars = allowSlashes ? ":*?\"><\n\r\t" : "\\/:*?\"><\n\r\t";
char* p = filename;
while (*p)
{
if (strchr(replaceChars, *p))
{
*p = cReplaceChar;
}
if (allowSlashes && *p == ALT_PATH_SEPARATOR)
{
*p = PATH_SEPARATOR;
}
p++;
}
// remove trailing dots and spaces. they are not allowed in directory names on windows,
// but we remove them on posix also, in a case the directory is accessed from windows via samba.
for (int len = strlen(filename); len > 0 && (filename[len - 1] == '.' || filename[len - 1] == ' '); len--)
{
filename[len - 1] = '\0';
}
}
// returns TRUE if the name was changed by adding duplicate-suffix
CString FileSystem::MakeUniqueFilename(const char* destDir, const char* basename)
{
CString result;
result.Format("%s%c%s", destDir, (int)PATH_SEPARATOR, basename);
int dupeNumber = 0;
while (FileExists(result))
{
dupeNumber++;
const char* extension = strrchr(basename, '.');
if (extension && extension != basename)
{
BString<1024> filenameWithoutExt = basename;
int end = extension - basename;
filenameWithoutExt[end < 1024 ? end : 1024-1] = '\0';
if (!strcasecmp(extension, ".par2"))
{
char* volExtension = strrchr(filenameWithoutExt, '.');
if (volExtension && volExtension != filenameWithoutExt &&
!strncasecmp(volExtension, ".vol", 4))
{
*volExtension = '\0';
extension = basename + (volExtension - filenameWithoutExt);
}
}
result.Format("%s%c%s.duplicate%d%s", destDir, (int)PATH_SEPARATOR,
*filenameWithoutExt, dupeNumber, extension);
}
else
{
result.Format("%s%c%s.duplicate%d", destDir, (int)PATH_SEPARATOR,
basename, dupeNumber);
}
}
return result;
}
bool FileSystem::MoveFile(const char* srcFilename, const char* dstFilename)
{
#ifdef WIN32
return _wrename(UtfPathToWidePath(srcFilename), UtfPathToWidePath(dstFilename)) == 0;
#else
bool ok = rename(srcFilename, dstFilename) == 0;
if (!ok && errno == EXDEV)
{
ok = CopyFile(srcFilename, dstFilename) && DeleteFile(srcFilename);
}
return ok;
#endif
}
bool FileSystem::CopyFile(const char* srcFilename, const char* dstFilename)
{
DiskFile infile;
if (!infile.Open(srcFilename, DiskFile::omRead))
{
return false;
}
DiskFile outfile;
if (!outfile.Open(dstFilename, DiskFile::omWrite))
{
return false;
}
CharBuffer buffer(1024 * 50);
int cnt = buffer.Size();
while (cnt == buffer.Size())
{
cnt = (int)infile.Read(buffer, buffer.Size());
outfile.Write(buffer, cnt);
}
infile.Close();
outfile.Close();
return true;
}
bool FileSystem::DeleteFile(const char* filename)
{
#ifdef WIN32
return _wremove(UtfPathToWidePath(filename)) == 0;
#else
return remove(filename) == 0;
#endif
}
bool FileSystem::FileExists(const char* filename)
{
#ifdef WIN32
// we use a native windows call because c-lib function "stat" fails on windows if file date is invalid
WIN32_FIND_DATAW findData;
HANDLE handle = FindFirstFileW(UtfPathToWidePath(filename), &findData);
if (handle != INVALID_HANDLE_VALUE)
{
bool exists = (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0;
FindClose(handle);
return exists;
}
return false;
#else
struct stat buffer;
bool exists = !stat(filename, &buffer) && S_ISREG(buffer.st_mode);
return exists;
#endif
}
bool FileSystem::FileExists(const char* path, const char* filenameWithoutPath)
{
BString<1024> fullFilename("%s%c%s", path, (int)PATH_SEPARATOR, filenameWithoutPath);
bool exists = FileExists(fullFilename);
return exists;
}
bool FileSystem::DirectoryExists(const char* dirFilename)
{
#ifdef WIN32
WIN32_FIND_DATAW findData;
// extra "\*" needed for network shares
HANDLE handle = FindFirstFileW(UtfPathToWidePath(
BString<1024>(dirFilename && dirFilename[strlen(dirFilename) - 1] == PATH_SEPARATOR ? "%s*" : "%s\\*", dirFilename)),
&findData);
if (handle != INVALID_HANDLE_VALUE)
{
bool exists = ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) ||
(strlen(dirFilename) == 3 && dirFilename[1] == ':');
FindClose(handle);
return exists;
}
return false;
#else
struct stat buffer;
bool exists = !stat(dirFilename, &buffer) && S_ISDIR(buffer.st_mode);
return exists;
#endif
}
bool FileSystem::CreateDirectory(const char* dirFilename)
{
#ifdef WIN32
_wmkdir(UtfPathToWidePath(dirFilename));
#else
mkdir(dirFilename, S_DIRMODE);
#endif
return DirectoryExists(dirFilename);
}
bool FileSystem::RemoveDirectory(const char* dirFilename)
{
#ifdef WIN32
return _wrmdir(UtfPathToWidePath(dirFilename)) == 0;
#else
return rmdir(dirFilename) == 0;
#endif
}
bool FileSystem::DeleteDirectoryWithContent(const char* dirFilename, CString& errmsg)
{
errmsg.Clear();
bool del = false;
bool ok = true;
{
DirBrowser dir(dirFilename);
while (const char* filename = dir.Next())
{
BString<1024> fullFilename("%s%c%s", dirFilename, PATH_SEPARATOR, filename);
if (FileSystem::DirectoryExists(fullFilename))
{
del = DeleteDirectoryWithContent(fullFilename, errmsg);
}
else
{
del = DeleteFile(fullFilename);
}
ok &= del;
if (!del && errmsg.Empty())
{
errmsg.Format("could not delete %s: %s", *fullFilename, *GetLastErrorMessage());
}
}
} // make sure "DirBrowser dir" is destroyed (and has closed its handle) before we trying to delete the directory
del = RemoveDirectory(dirFilename);
ok &= del;
if (!del && errmsg.Empty())
{
errmsg = GetLastErrorMessage();
}
return ok;
}
int64 FileSystem::FileSize(const char* filename)
{
#ifdef WIN32
// we use a native windows call because c-lib function "stat" fails on windows if file date is invalid
WIN32_FIND_DATAW findData;
HANDLE handle = FindFirstFileW(UtfPathToWidePath(filename), &findData);
if (handle != INVALID_HANDLE_VALUE)
{
int64 size = ((int64)(findData.nFileSizeHigh) << 32) + findData.nFileSizeLow;
FindClose(handle);
return size;
}
return -1;
#else
struct stat buffer;
stat(filename, &buffer);
return buffer.st_size;
#endif
}
int64 FileSystem::FreeDiskSize(const char* path)
{
#ifdef WIN32
ULARGE_INTEGER free, dummy;
if (GetDiskFreeSpaceEx(path, &free, &dummy, &dummy))
{
return free.QuadPart;
}
#else
struct statvfs diskdata;
if (!statvfs(path, &diskdata))
{
return (int64)diskdata.f_frsize * (int64)diskdata.f_bavail;
}
#endif
return -1;
}
bool FileSystem::RenameBak(const char* filename, const char* bakPart, bool removeOldExtension, CString& newName)
{
BString<1024> changedFilename;
if (removeOldExtension)
{
changedFilename = filename;
char* extension = strrchr(changedFilename, '.');
if (extension)
{
*extension = '\0';
}
}
newName.Format("%s.%s", removeOldExtension ? *changedFilename : filename, bakPart);
int i = 2;
while (FileExists(newName) || DirectoryExists(newName))
{
newName.Format("%s.%i.%s", removeOldExtension ? *changedFilename : filename, i++, bakPart);
}
return MoveFile(filename, newName);
}
#ifndef WIN32
CString FileSystem::ExpandHomePath(const char* filename)
{
CString result;
if (filename && (filename[0] == '~') && (filename[1] == '/'))
{
// expand home-dir
char* home = getenv("HOME");
if (!home)
{
struct passwd *pw = getpwuid(getuid());
if (pw)
{
home = pw->pw_dir;
}
}
if (!home)
{
return filename;
}
if (home[strlen(home)-1] == '/')
{
result.Format("%s%s", home, filename + 2);
}
else
{
result.Format("%s/%s", home, filename + 2);
}
}
else
{
result.Append(filename ? filename : "");
}
return result;
}
#endif
CString FileSystem::ExpandFileName(const char* filename)
{
#ifdef WIN32
wchar_t unistr[1024];
_wfullpath(unistr, UtfPathToWidePath(filename), 1024);
return WidePathToUtfPath(unistr);
#else
CString result;
result.Reserve(1024 - 1);
if (filename[0] != '\0' && filename[0] != '/')
{
char curDir[MAX_PATH + 1];
getcwd(curDir, sizeof(curDir) - 1); // 1 char reserved for adding backslash
int offset = 0;
if (filename[0] == '.' && filename[1] == '/')
{
offset += 2;
}
result.Format("%s/%s", curDir, filename + offset);
}
else
{
result = filename;
}
return result;
#endif
}
CString FileSystem::GetExeFileName(const char* argv0)
{
CString exename;
exename.Reserve(1024 - 1);
exename[1024 - 1] = '\0';
#ifdef WIN32
GetModuleFileName(nullptr, exename, 1024);
#else
// Linux
int r = readlink("/proc/self/exe", exename, 1024 - 1);
if (r > 0)
{
exename[r] = '\0';
return exename;
}
// FreeBSD
r = readlink("/proc/curproc/file", exename, 1024 - 1);
if (r > 0)
{
exename[r] = '\0';
return exename;
}
exename = ExpandFileName(argv0);
#endif
return exename;
}
bool FileSystem::SameFilename(const char* filename1, const char* filename2)
{
#ifdef WIN32
return strcasecmp(filename1, filename2) == 0;
#else
return strcmp(filename1, filename2) == 0;
#endif
}
#ifdef WIN32
CString FileSystem::MakeCanonicalPath(const char* path)
{
int len = strlen(path);
if (!strncmp("\\\\?\\", path, 4) || len == 0)
{
return path;
}
std::vector<CString> components = Util::SplitStr(path, "\\/");
for (uint32 i = 1; i < components.size(); i++)
{
if (!strcmp(components[i], ".."))
{
components.erase(components.begin() + i - 1, components.begin() + i + 1);
i -= 2;
}
else if (!strcmp(components[i], "."))
{
components.erase(components.begin() + i);
i--;
}
}
StringBuilder result;
result.Reserve(strlen(path));
if (!strncmp("\\\\", path, 2))
{
result.Append("\\\\");
}
bool first = true;
for (CString& comp : components)
{
if (comp.Length() > 0)
{
if (!first)
{
result.Append("\\");
}
result.Append(comp);
first = false;
}
}
if ((path[len - 1] == '\\' || path[len - 1] == '/' ||
(len > 3 && !strcmp(path + len - 3, "\\..")) ||
(len > 2 && !strcmp(path + len - 2, "\\.")))
&&
result[result.Length() - 1] != '\\')
{
result.Append("\\");
}
return *result;
}
#endif
bool FileSystem::FlushFileBuffers(int fileDescriptor, CString& errmsg)
{
#ifdef WIN32
BOOL ok = ::FlushFileBuffers((HANDLE)_get_osfhandle(fileDescriptor));
if (!ok)
{
errmsg.Reserve(1024 - 1);
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
errmsg, 1024, nullptr);
}
return ok;
#else
#ifdef HAVE_FULLFSYNC
int ret = fcntl(fileDescriptor, F_FULLFSYNC) == -1 ? 1 : 0;
#elif HAVE_FDATASYNC
int ret = fdatasync(fileDescriptor);
#else
int ret = fsync(fileDescriptor);
#endif
if (ret != 0)
{
errmsg = GetLastErrorMessage();
}
return ret == 0;
#endif
}
bool FileSystem::FlushDirBuffers(const char* filename, CString& errmsg)
{
#ifdef WIN32
FILE* file = _wfopen(UtfPathToWidePath(filename), WString(FOPEN_RBP));
#else
BString<1024> parentPath = filename;
char* p = (char*)strrchr(parentPath, PATH_SEPARATOR);
if (p)
{
*p = '\0';
}
FILE* file = fopen(parentPath, FOPEN_RB);
#endif
if (!file)
{
errmsg = GetLastErrorMessage();
return false;
}
bool ok = FlushFileBuffers(fileno(file), errmsg);
fclose(file);
return ok;
}
#ifndef WIN32
void FileSystem::FixExecPermission(const char* filename)
{
struct stat buffer;
bool ok = !stat(filename, &buffer);
if (ok)
{
buffer.st_mode = buffer.st_mode | S_IXUSR | S_IXGRP | S_IXOTH;
chmod(filename, buffer.st_mode);
}
}
#endif
CString FileSystem::MakeExtendedPath(const char* path, bool force)
{
#ifdef WIN32
if (force || strlen(path) > 260 - 14)
{
CString canonicalPath = MakeCanonicalPath(path);
BString<1024> longpath;
if (canonicalPath[0] == '\\' && canonicalPath[1] == '\\')
{
// UNC path
longpath.Format("\\\\?\\UNC\\%s", canonicalPath + 2);
}
else
{
// local path
longpath.Format("\\\\?\\%s", canonicalPath);
}
return *longpath;
}
else
#endif
{
return path;
}
}
#ifdef WIN32
WString FileSystem::UtfPathToWidePath(const char* utfpath)
{
return *FileSystem::MakeExtendedPath(utfpath, false);
}
CString FileSystem::WidePathToUtfPath(const wchar_t* wpath)
{
char utfstr[1024];
int copied = WideCharToMultiByte(CP_UTF8, 0, wpath, -1, utfstr, 1024, nullptr, nullptr);
return utfstr;
}
#endif
#ifdef WIN32
DirBrowser::DirBrowser(const char* path)
{
BString<1024> mask("%s%c*.*", path, (int)PATH_SEPARATOR);
m_file = FindFirstFileW(FileSystem::UtfPathToWidePath(mask), &m_findData);
m_first = true;
}
DirBrowser::~DirBrowser()
{
if (m_file != INVALID_HANDLE_VALUE)
{
FindClose(m_file);
}
}
const char* DirBrowser::InternNext()
{
bool ok = false;
if (m_first)
{
ok = m_file != INVALID_HANDLE_VALUE;
m_first = false;
}
else
{
ok = FindNextFileW(m_file, &m_findData) != 0;
}
if (ok)
{
m_filename = FileSystem::WidePathToUtfPath(m_findData.cFileName);
return m_filename;
}
return nullptr;
}
#else
#ifdef DIRBROWSER_SNAPSHOT
DirBrowser::DirBrowser(const char* path, bool snapshot) :
m_snapshot(snapshot)
#else
DirBrowser::DirBrowser(const char* path)
#endif
{
#ifdef DIRBROWSER_SNAPSHOT
if (m_snapshot)
{
DirBrowser dir(path, false);
while (const char* filename = dir.Next())
{
m_snapshotFiles.emplace_back(filename);
}
m_snapshotIter = m_snapshotFiles.begin();
}
else
#endif
{
m_dir = opendir(path);
}
}
DirBrowser::~DirBrowser()
{
#ifdef DIRBROWSER_SNAPSHOT
if (!m_snapshot)
#endif
{
if (m_dir)
{
closedir(m_dir);
}
}
}
const char* DirBrowser::InternNext()
{
#ifdef DIRBROWSER_SNAPSHOT
if (m_snapshot)
{
return m_snapshotIter == m_snapshotFiles.end() ? nullptr : **m_snapshotIter++;
}
else
#endif
{
if (m_dir)
{
m_findData = readdir(m_dir);
if (m_findData)
{
return m_findData->d_name;
}
}
return nullptr;
}
}
#endif
const char* DirBrowser::Next()
{
const char* filename = nullptr;
for (filename = InternNext(); filename && (!strcmp(filename, ".") || !strcmp(filename, "..")); )
{
filename = InternNext();
}
return filename;
}
DiskFile::~DiskFile()
{
if (m_file)
{
Close();
}
}
bool DiskFile::Open(const char* filename, EOpenMode mode)
{
const char* strmode = mode == omRead ? FOPEN_RB : mode == omReadWrite ?
FOPEN_RBP : mode == omWrite ? FOPEN_WB : FOPEN_AB;
#ifdef WIN32
m_file = _wfopen(FileSystem::UtfPathToWidePath(filename), WString(strmode));
#else
m_file = fopen(filename, strmode);
#endif
return m_file;
}
bool DiskFile::Close()
{
if (m_file)
{
int ret = fclose(m_file);
m_file = nullptr;
return ret;
}
else
{
return false;
}
}
int64 DiskFile::Read(void* buffer, int64 size)
{
return fread(buffer, 1, (size_t)size, m_file);
}
int64 DiskFile::Write(const void* buffer, int64 size)
{
return fwrite(buffer, 1, (size_t)size, m_file);
}
int64 DiskFile::Print(const char* format, ...)
{
va_list ap;
va_start(ap, format);
int ret = vfprintf(m_file, format, ap);
va_end(ap);
return ret;
}
char* DiskFile::ReadLine(char* buffer, int64 size)
{
return fgets(buffer, (int)size, m_file);
}
int64 DiskFile::Position()
{
return ftell(m_file);
}
int64 DiskFile::Seek(int64 position, ESeekOrigin origin)
{
return fseek(m_file, position,
origin == soCur ? SEEK_CUR :
origin == soEnd ? SEEK_END : SEEK_SET) == 0;
}
bool DiskFile::Eof()
{
return feof(m_file) != 0;
}
bool DiskFile::Error()
{
return ferror(m_file) != 0;
}
bool DiskFile::SetWriteBuffer(int size)
{
return setvbuf(m_file, nullptr, _IOFBF, size) == 0;
}
bool DiskFile::Flush()
{
return fflush(m_file) == 0;
}
bool DiskFile::Sync(CString& errmsg)
{
return FileSystem::FlushFileBuffers(fileno(m_file), errmsg);
}