You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

686 lines
17 KiB

////////////////////////////////////////////////////////////////////
// File.h
//
// Copyright 2007 cDc@seacave
// Distributed under the Boost Software License, Version 1.0
// (See http://www.boost.org/LICENSE_1_0.txt)
#ifndef __SEACAVE_FILE_H__
#define __SEACAVE_FILE_H__
// I N C L U D E S /////////////////////////////////////////////////
#include "Streams.h"
// Under both Windows and Unix, the stat function is used for classification
// Under Gnu/Linux, the following classifications are defined
// source: Gnu/Linux man page for stat(2) http://linux.die.net/man/2/stat
// S_IFMT 0170000 bitmask for the file type bitfields
// S_IFSOCK 0140000 socket (Note this overlaps with S_IFDIR)
// S_IFLNK 0120000 symbolic link
// S_IFREG 0100000 regular file
// S_IFBLK 0060000 block device
// S_IFDIR 0040000 directory
// S_IFCHR 0020000 character device
// S_IFIFO 0010000 FIFO
// There are also some Posix-standard macros:
// S_ISREG(m) is it a regular file?
// S_ISDIR(m) directory?
// S_ISCHR(m) character device?
// S_ISBLK(m) block device?
// S_ISFIFO(m) FIFO (named pipe)?
// S_ISLNK(m) symbolic link? (Not in POSIX.1-1996.)
// S_ISSOCK(m) socket? (Not in POSIX.1-1996.)
// Under Windows, the following are defined:
// source: Header file sys/stat.h distributed with Visual Studio 10
// _S_IFMT (S_IFMT) 0xF000 file type mask
// _S_IFREG (S_IFREG) 0x8000 regular
// _S_IFDIR (S_IFDIR) 0x4000 directory
// _S_IFCHR (S_IFCHR) 0x2000 character special
// _S_IFIFO 0x1000 pipe
#ifdef _MSC_VER
#include <io.h>
// file type tests are not defined for some reason on Windows despite them providing the stat() function!
#define F_OK 0
#define X_OK 1
#define W_OK 2
#define R_OK 4
// Posix-style macros for Windows
#ifndef S_ISREG
#define S_ISREG(mode) ((mode & _S_IFMT) == _S_IFREG)
#endif
#ifndef S_ISDIR
#define S_ISDIR(mode) ((mode & _S_IFMT) == _S_IFDIR)
#endif
#ifndef S_ISCHR
#define S_ISCHR(mode) ((mode & _S_IFMT) == _S_IFCHR)
#endif
#ifndef S_ISBLK
#define S_ISBLK(mode) (false)
#endif
#ifndef S_ISFIFO
#define S_ISFIFO(mode) ((mode & _S_IFMT) == _S_IFIFO)
#endif
#ifndef S_ISLNK
#define S_ISLNK(mode) (false)
#endif
#ifndef S_ISSOCK
#define S_ISSOCK(mode) (false)
#endif
#else
#include <unistd.h>
#include <dirent.h>
#define _taccess access
#endif
#ifdef __APPLE__
#define fdatasync fsync
#endif
// D E F I N E S ///////////////////////////////////////////////////
// size of the stored file size variable
#ifdef LARGEFILESIZE
#define FILESIZE size_f_t
#else
#define FILESIZE size_t
#endif
// invalid file handle
#ifdef _MSC_VER
#define FILE_INVALID_HANDLE INVALID_HANDLE_VALUE
#else
#define FILE_INVALID_HANDLE int(-1)
#endif
namespace SEACAVE {
// S T R U C T S ///////////////////////////////////////////////////
class GENERAL_API File : public IOStream {
public:
typedef struct FILEINFO_TYPE {
String path;
FILESIZE size;
DWORD attrib;
} FILEINFO;
typedef cList<FILEINFO> FileInfoArr;
typedef enum FMCREATE_TYPE {
OPEN = 0x01,
CREATE = 0x02,
TRUNCATE = 0x04
} FMCREATE;
typedef enum FMFLAGS_TYPE {
SYNC = 0x01,
NOBUFFER = 0x02,
RANDOM = 0x04,
SEQUENTIAL = 0x08
} FMFLAGS;
inline File() : h(FILE_INVALID_HANDLE) {
#ifndef _RELEASE
breakRead = -1;
breakWrite = -1;
#endif
}
inline File(LPCTSTR aFileName, int access, int mode, int flags=0) : h(FILE_INVALID_HANDLE) {
#ifndef _RELEASE
breakRead = -1;
breakWrite = -1;
#endif
File::open(aFileName, access, mode, flags);
}
virtual ~File() {
File::close();
}
#ifdef _SUPPORT_CPP11
inline File(File&& rhs) : h(rhs.h) {
#ifndef _RELEASE
breakRead = rhs.breakRead;
breakWrite = rhs.breakWrite;
#endif
rhs.h = FILE_INVALID_HANDLE;
}
inline File& operator=(File&& rhs) {
h = rhs.h;
#ifndef _RELEASE
breakRead = rhs.breakRead;
breakWrite = rhs.breakWrite;
#endif
rhs.h = FILE_INVALID_HANDLE;
return *this;
}
#endif
bool isOpen() const {
return h != FILE_INVALID_HANDLE;
}
#ifdef _MSC_VER
typedef enum FMACCESS_TYPE {
READ = GENERIC_READ,
WRITE = GENERIC_WRITE,
RW = READ | WRITE
} FMACCESS;
typedef enum FMCHECKACCESS_TYPE {
CA_EXIST = F_OK, // existence
CA_WRITE = W_OK, // write
CA_READ = R_OK, // read
CA_RW = R_OK | W_OK,
} FMCHECKACCESS;
/**
* Open the file specified.
* If there are errors, h is set to FILE_INVALID_HANDLE.
* Use isOpen() to check.
*/
virtual void open(LPCTSTR aFileName, int access, int mode, int flags=0) {
ASSERT(access == WRITE || access == READ || access == (READ | WRITE));
close();
DWORD m = 0;
if (mode & OPEN) {
if (mode & CREATE) {
m = (mode & TRUNCATE) ? CREATE_ALWAYS : OPEN_ALWAYS;
} else {
m = (mode & TRUNCATE) ? TRUNCATE_EXISTING : OPEN_EXISTING;
}
} else {
ASSERT(mode & CREATE);
m = (mode & TRUNCATE) ? CREATE_ALWAYS : CREATE_NEW;
}
DWORD f = 0;
if (flags & SYNC)
f |= FILE_FLAG_WRITE_THROUGH;
if (flags & NOBUFFER)
f |= FILE_FLAG_NO_BUFFERING;
if (flags & RANDOM)
f |= FILE_FLAG_RANDOM_ACCESS;
if (flags & SEQUENTIAL)
f |= FILE_FLAG_SEQUENTIAL_SCAN;
h = ::CreateFile(aFileName, access, FILE_SHARE_READ, NULL, m, f, NULL);
}
virtual void close() {
if (isOpen()) {
FlushFileBuffers(h);
CloseHandle(h);
h = FILE_INVALID_HANDLE;
}
}
uint32_t getLastModified() {
ASSERT(isOpen());
FILETIME f = {0};
::GetFileTime(h, NULL, NULL, &f);
return convertTime(&f);
}
static uint32_t convertTime(FILETIME* f) {
SYSTEMTIME s = { 1970, 1, 0, 1, 0, 0, 0, 0 };
FILETIME f2 = {0};
if (::SystemTimeToFileTime(&s, &f2)) {
uint64_t* a = (uint64_t*)f;
uint64_t* b = (uint64_t*)&f2;
*a -= *b;
*a /= (1000LL*1000LL*1000LL/100LL); // 100ns > s
return (uint32_t)*a;
}
return 0;
}
size_f_t getSize() const override {
ASSERT(isOpen());
DWORD x;
DWORD l = ::GetFileSize(h, &x);
if ((l == INVALID_FILE_SIZE) && (GetLastError() != NO_ERROR))
return SIZE_NA;
return (size_f_t)l | ((size_f_t)x)<<32;
}
virtual bool setSize(size_f_t newSize) {
const size_f_t pos = getPos();
if (pos == SIZE_NA)
return false;
if (!setPos(newSize))
return false;
if (!setEOF())
return false;
if (!setPos(pos))
return false;
return true;
}
size_f_t getPos() const override {
ASSERT(isOpen());
LONG x = 0;
const DWORD l = ::SetFilePointer(h, 0, &x, FILE_CURRENT);
if (l == INVALID_SET_FILE_POINTER)
return SIZE_NA;
return (size_f_t)l | ((size_f_t)x)<<32;
}
bool setPos(size_f_t pos) override {
ASSERT(isOpen());
LONG x = (LONG) (pos>>32);
return (::SetFilePointer(h, (DWORD)(pos & 0xffffffff), &x, FILE_BEGIN) != INVALID_SET_FILE_POINTER);
}
virtual bool setEndPos(size_f_t pos) {
ASSERT(isOpen());
LONG x = (LONG) (pos>>32);
return (::SetFilePointer(h, (DWORD)(pos & 0xffffffff), &x, FILE_END) != INVALID_SET_FILE_POINTER);
}
virtual bool movePos(size_f_t pos) {
ASSERT(isOpen());
LONG x = (LONG) (pos>>32);
return (::SetFilePointer(h, (DWORD)(pos & 0xffffffff), &x, FILE_CURRENT) != INVALID_SET_FILE_POINTER);
}
size_t read(void* buf, size_t len) override {
ASSERT(isOpen());
#ifndef _RELEASE
if (breakRead != (size_t)(-1)) {
if (breakRead <= len) {
ASSERT("FILE::read() break" == NULL);
breakRead = -1;
} else {
breakRead -= len;
}
}
#endif
DWORD x;
if (!::ReadFile(h, buf, (DWORD)len, &x, NULL))
return STREAM_ERROR;
return x;
}
size_t write(const void* buf, size_t len) override {
ASSERT(isOpen());
#ifndef _RELEASE
if (breakWrite != (size_t)(-1)) {
if (breakWrite <= len) {
ASSERT("FILE::write() break" == NULL);
breakWrite = -1;
} else {
breakWrite -= len;
}
}
#endif
DWORD x;
if (!::WriteFile(h, buf, (DWORD)len, &x, NULL))
return STREAM_ERROR;
ASSERT(x == len);
return x;
}
virtual bool setEOF() {
ASSERT(isOpen());
return (SetEndOfFile(h) != FALSE);
}
size_t flush() override {
ASSERT(isOpen());
return (FlushFileBuffers(h) ? 0 : STREAM_ERROR);
}
virtual bool getInfo(BY_HANDLE_FILE_INFORMATION* fileInfo) {
ASSERT(isOpen());
return (GetFileInformationByHandle(h, fileInfo) != FALSE);
}
static uint32_t getAttrib(LPCTSTR aFileName) {
return GetFileAttributes(aFileName);
}
static bool setAttrib(LPCTSTR aFileName, uint32_t attribs) {
return (SetFileAttributes(aFileName, attribs) != FALSE);
}
static void deleteFile(LPCTSTR aFileName) { ::DeleteFile(aFileName); }
static bool renameFile(LPCTSTR source, LPCTSTR target) {
if (!::MoveFile(source, target)) {
// Can't move, try copy/delete...
if (!::CopyFile(source, target, FALSE))
return false;
deleteFile(source);
}
return true;
}
static bool copyFile(LPCTSTR source, LPCTSTR target) { return ::CopyFile(source, target, FALSE) == TRUE; }
static size_f_t getSize(LPCTSTR aFileName) {
const HANDLE fh = ::CreateFile(aFileName, FILE_READ_ATTRIBUTES, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_NO_BUFFERING, NULL);
if (fh == FILE_INVALID_HANDLE)
return SIZE_NA;
DWORD x;
DWORD l = ::GetFileSize(fh, &x);
CloseHandle(fh);
if ((l == INVALID_FILE_SIZE) && (GetLastError() != NO_ERROR))
return SIZE_NA;
return (((size_f_t)l) | (((size_f_t)x)<<32));
}
static size_f_t findFiles(const String& _strPath, const String& strMask, bool bProcessSubdir, FileInfoArr& arrFiles) {
// List all the files.
WIN32_FIND_DATA fd;
HANDLE hFind;
size_f_t totalSize = 0;
String strPath(_strPath);
Util::ensureFolderSlash(strPath);
//Find all the files in this folder.
hFind = FindFirstFile((strPath + strMask).c_str(), &fd);
if (hFind != FILE_INVALID_HANDLE) {
do {
// this is a file that can be used
if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
continue;
// Store the file name.
FILEINFO& fileInfo = arrFiles.AddEmpty();
fileInfo.path = strPath + fd.cFileName;
#ifdef LARGEFILESIZE
fileInfo.size = (((size_f_t)fd.nFileSizeLow) | (((size_f_t)fd.nFileSizeHigh)<<32));
#else
fileInfo.size = fd.nFileSizeLow;
#endif
fileInfo.attrib = fd.dwFileAttributes;
totalSize += fileInfo.size;
} while (FindNextFile(hFind, &fd));
FindClose(hFind);
}
//Process the subfolders also...
if (!bProcessSubdir)
return totalSize;
hFind = FindFirstFile((strPath + '*').c_str(), &fd);
if (hFind != FILE_INVALID_HANDLE) {
do {
// if SUBDIR then process that too
if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
continue;
if (!_tcscmp(fd.cFileName, _T(".")))
continue;
if (!_tcscmp(fd.cFileName, _T("..")))
continue;
// Process all subfolders recursively
totalSize += findFiles(strPath + fd.cFileName + PATH_SEPARATOR, strMask, true, arrFiles);
} while (FindNextFile(hFind, &fd));
FindClose(hFind);
}
return totalSize;
}
#else // _MSC_VER
typedef enum FMACCESS_TYPE {
READ = 0x01,
WRITE = 0x02,
RW = READ | WRITE,
} FMACCESS;
typedef enum FMCHECKACCESS_TYPE {
CA_EXIST = F_OK, // existence
CA_WRITE = W_OK, // write
CA_READ = R_OK, // read
CA_RW = R_OK | W_OK,
CA_EXEC = X_OK, // execute
} FMCHECKACCESS;
/**
* Open the file specified.
* If there are errors, h is set to FILE_INVALID_HANDLE.
* Use isOpen() to check.
*/
virtual void open(LPCTSTR aFileName, int access, int mode, int flags=0) {
ASSERT(access == WRITE || access == READ || access == (READ | WRITE));
close();
int m = 0;
if (access == READ)
m |= O_RDONLY;
else if (access == WRITE)
m |= O_WRONLY;
else
m |= O_RDWR;
if (mode & CREATE)
m |= O_CREAT;
if (mode & TRUNCATE)
m |= O_TRUNC;
if (flags & SYNC)
m |= O_DSYNC;
#ifndef __APPLE__
if (flags & NOBUFFER)
m |= O_DIRECT;
#endif
h = ::open(aFileName, m, S_IRUSR | S_IWUSR);
}
virtual void close() {
if (h != FILE_INVALID_HANDLE) {
::close(h);
h = FILE_INVALID_HANDLE;
}
}
uint32_t getLastModified() {
ASSERT(isOpen());
struct stat s;
if (::fstat(h, &s) == -1)
return 0;
return (uint32_t)s.st_mtime;
}
size_f_t getSize() const override {
ASSERT(isOpen());
struct stat s;
if (::fstat(h, &s) == -1)
return SIZE_NA;
return (size_f_t)s.st_size;
}
size_f_t getPos() const override {
ASSERT(isOpen());
return (size_f_t)lseek(h, 0, SEEK_CUR);
}
bool setPos(size_f_t pos) override { ASSERT(isOpen()); return lseek(h, (off_t)pos, SEEK_SET) != (off_t)-1; }
virtual bool setEndPos(size_f_t pos) { ASSERT(isOpen()); return lseek(h, (off_t)pos, SEEK_END) != (off_t)-1; }
virtual bool movePos(size_f_t pos) { ASSERT(isOpen()); return lseek(h, (off_t)pos, SEEK_CUR) != (off_t)-1; }
size_t read(void* buf, size_t len) override {
ASSERT(isOpen());
#ifndef _RELEASE
if (breakRead != (size_t)(-1)) {
if (breakRead <= len) {
ASSERT("FILE::read() break" == NULL);
breakRead = -1;
} else {
breakRead -= len;
}
}
#endif
ssize_t x = ::read(h, buf, len);
if (x == -1)
return STREAM_ERROR;
return (size_t)x;
}
size_t write(const void* buf, size_t len) override {
ASSERT(isOpen());
#ifndef _RELEASE
if (breakWrite != (size_t)(-1)) {
if (breakWrite <= len) {
ASSERT("FILE::write() break" == NULL);
breakWrite = -1;
} else {
breakWrite -= len;
}
}
#endif
ssize_t x = ::write(h, buf, len);
if (x == -1)
return STREAM_ERROR;
if (x < (ssize_t)len)
return STREAM_ERROR;
return x;
}
virtual bool setEOF() {
ASSERT(isOpen());
return (ftruncate(h, (off_t)getPos()) != -1);
}
virtual bool setSize(size_f_t newSize) {
ASSERT(isOpen());
return (ftruncate(h, (off_t)newSize) != -1);
}
size_t flush() override {
ASSERT(isOpen());
return fdatasync(h);
}
static void deleteFile(LPCTSTR aFileName) { ::remove(aFileName); }
static bool renameFile(LPCTSTR source, LPCTSTR target) { return ::rename(source, target) == 0; }
static bool copyFile(LPCTSTR source, LPCTSTR target) {
std::ifstream src(source, std::ios::binary);
if (!src.is_open())
return false;
std::ofstream dst(target, std::ios::binary);
if (!dst.is_open())
return false;
dst << src.rdbuf();
return true;
}
static size_f_t getSize(LPCTSTR aFileName) {
struct stat buf;
if (stat(aFileName, &buf) != 0)
return SIZE_NA;
return buf.st_size;
}
#endif // _MSC_VER
// test for whether there's something (i.e. folder or file) with this name
// and what access mode is supported
static bool isPresent(LPCTSTR path) {
struct stat buf;
return stat(path, &buf) == 0;
}
static bool access(LPCTSTR path, int mode=CA_EXIST) {
return ::_taccess(path, mode) == 0;
}
// test for whether there's something present and its a folder
static bool isFolder(LPCTSTR path) {
struct stat buf;
if (!(stat(path, &buf) == 0))
return false;
// If the object is present, see if it is a directory
// this is the Posix-approved way of testing
return S_ISDIR(buf.st_mode);
}
// test for whether there's something present and its a file
// a file can be a regular file, a symbolic link, a FIFO or a socket, but not a device
static bool isFile(LPCTSTR path) {
struct stat buf;
if (!(stat(path, &buf) == 0))
return false;
// If the object is present, see if it is a file or file-like object
// Note that devices are neither folders nor files
// this is the Posix-approved way of testing
return S_ISREG(buf.st_mode) || S_ISLNK(buf.st_mode) || S_ISSOCK(buf.st_mode) || S_ISFIFO(buf.st_mode);
}
// time the file was originally created
static time_t getCreated(LPCTSTR path) {
struct stat buf;
if (stat(path, &buf) != 0) return 0;
return buf.st_ctime;
}
// time the file was last modified
static time_t getModified(LPCTSTR path) {
struct stat buf;
if (stat(path, &buf) != 0) return 0;
return buf.st_mtime;
}
// time the file was accessed
static time_t getAccessed(LPCTSTR path) {
struct stat buf;
if (stat(path, &buf) != 0) return 0;
return buf.st_atime;
}
// set the current folder
static bool setCurrentFolder(LPCTSTR path) {
if (!isFolder(path))
return false;
#ifdef _MSC_VER
// Windows implementation - this returns non-zero for success
return (SetCurrentDirectory(path) != 0);
#else
// Unix implementation - this returns zero for success
return (chdir(path) == 0);
#endif
}
template <class VECTOR>
inline size_t write(const VECTOR& arr) {
const typename VECTOR::IDX nSize(arr.GetSize());
size_t nBytes(write(&nSize, sizeof(typename VECTOR::IDX)));
nBytes += write(arr.GetData(), arr.GetDataSize());
return nBytes;
}
template <class VECTOR>
inline size_t read(VECTOR& arr) {
typename VECTOR::IDX nSize;
size_t nBytes(read(&nSize, sizeof(typename VECTOR::IDX)));
arr.Resize(nSize);
nBytes += read(arr.GetData(), arr.GetDataSize());
return nBytes;
}
enum { LAYER_ID_IN=3 };
InputStream* getInputStream(int typ=InputStream::LAYER_ID_IN) override { return (typ == LAYER_ID_IN ? static_cast<InputStream*>(this) : IOStream::getInputStream(typ)); }
enum { LAYER_ID_OUT=3 };
OutputStream* getOutputStream(int typ=OutputStream::LAYER_ID_OUT) override { return (typ == LAYER_ID_OUT ? static_cast<OutputStream*>(this) : IOStream::getOutputStream(typ)); }
protected:
#ifdef _MSC_VER
HANDLE h;
#else
int h;
#endif
public:
#ifndef _RELEASE
size_t breakRead;
size_t breakWrite;
#endif
private:
File(const File&);
File& operator=(const File&);
};
typedef File* LPFILE;
/*----------------------------------------------------------------*/
} // namespace SEACAVE
#endif // __SEACAVE_FILE_H__