//////////////////////////////////////////////////////////////////// // 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 // 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 #include #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 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 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 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(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(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__