www.pudn.com > DCPlusPlus-src.zip > File.h
/* * Copyright (C) 2001-2004 Jacek Sieka, j_s at telia com * * 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, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #if !defined(AFX_FILE_H__CB551CD7_189C_4175_922E_8B00B4C8D6F1__INCLUDED_) #define AFX_FILE_H__CB551CD7_189C_4175_922E_8B00B4C8D6F1__INCLUDED_ #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 #include "SettingsManager.h" #include "Exception.h" #include "Util.h" #include "Text.h" #ifndef _WIN32 #include#include #include #endif STANDARD_EXCEPTION(FileException); #ifdef _WIN32 #include "../zlib/zlib.h" #else #include #endif /** * A naive output stream. We don't use the stl ones to avoid compiling STLPort, * besides this is a lot more lightweight (and less flexible)... */ class OutputStream { public: virtual ~OutputStream() { } /** * @return The actual number of bytes written. len bytes will always be * consumed, but fewer or more bytes may actually be written, * for example if the stream is being compressed. */ virtual size_t write(const void* buf, size_t len) throw(Exception) = 0; /** * This must be called before destroying the object to make sure all data * is properly written (we don't want destructors that throw exceptions * and the last flush might actually throw). Note that some implementations * might not need it... */ virtual size_t flush() throw(Exception) = 0; size_t write(const string& str) throw(Exception) { return write(str.c_str(), str.size()); }; }; class InputStream { public: virtual ~InputStream() { } /** * Call this function until it returns 0 to get all bytes. * @return The number of bytes read. len reflects the number of bytes * actually read from the stream source in this call. */ virtual size_t read(void* buf, size_t& len) throw(Exception) = 0; }; class IOStream : public InputStream, public OutputStream { }; class File : public IOStream { public: enum { OPEN = 0x01, CREATE = 0x02, TRUNCATE = 0x04 }; #ifdef _WIN32 enum { READ = GENERIC_READ, WRITE = GENERIC_WRITE, RW = READ | WRITE }; File(const string& aFileName, int access, int mode) throw(FileException) { dcassert(access == WRITE || access == READ || access == (READ | WRITE)); int m = 0; if(mode & OPEN) { if(mode & CREATE) { m = (mode & TRUNCATE) ? CREATE_ALWAYS : OPEN_ALWAYS; } else { m = (mode & TRUNCATE) ? TRUNCATE_EXISTING : OPEN_EXISTING; } } else { if(mode & CREATE) { m = (mode & TRUNCATE) ? CREATE_ALWAYS : CREATE_NEW; } else { dcassert(0); } } h = ::CreateFile(Text::utf8ToWide(aFileName).c_str(), access, FILE_SHARE_READ, NULL, m, FILE_FLAG_SEQUENTIAL_SCAN, NULL); if(h == INVALID_HANDLE_VALUE) { throw FileException(Util::translateError(GetLastError())); } } u_int32_t getLastModified() { FILETIME f = {0}; ::GetFileTime(h, NULL, NULL, &f); return convertTime(&f); } static u_int32_t convertTime(FILETIME* f) { SYSTEMTIME s = { 1970, 1, 0, 1, 0, 0, 0, 0 }; FILETIME f2 = {0}; if(::SystemTimeToFileTime(&s, &f2)) { u_int64_t* a = (u_int64_t*)f; u_int64_t* b = (u_int64_t*)&f2; *a -= *b; *a /= (1000LL*1000LL*1000LL/100LL); // 100ns > s return (u_int32_t)*a; } return 0; } bool isOpen() { return h != INVALID_HANDLE_VALUE; }; virtual void close() throw() { if(isOpen()) { CloseHandle(h); h = INVALID_HANDLE_VALUE; } } virtual int64_t getSize() throw() { DWORD x; DWORD l = ::GetFileSize(h, &x); if( (l == INVALID_FILE_SIZE) && (GetLastError() != NO_ERROR)) return -1; return (int64_t)l | ((int64_t)x)<<32; } virtual void setSize(int64_t newSize) throw(FileException) { int64_t pos = getPos(); setPos(newSize); setEOF(); setPos(pos); } virtual int64_t getPos() throw() { LONG x = 0; DWORD l = ::SetFilePointer(h, 0, &x, FILE_CURRENT); return (int64_t)l | ((int64_t)x)<<32; } virtual void setPos(int64_t pos) throw() { LONG x = (LONG) (pos>>32); ::SetFilePointer(h, (DWORD)(pos & 0xffffffff), &x, FILE_BEGIN); } virtual void setEndPos(int64_t pos) throw() { LONG x = (LONG) (pos>>32); ::SetFilePointer(h, (DWORD)(pos & 0xffffffff), &x, FILE_END); } virtual void movePos(int64_t pos) throw() { LONG x = (LONG) (pos>>32); ::SetFilePointer(h, (DWORD)(pos & 0xffffffff), &x, FILE_CURRENT); } virtual size_t read(void* buf, size_t& len) throw(Exception) { DWORD x; if(!::ReadFile(h, buf, (DWORD)len, &x, NULL)) { throw(FileException(Util::translateError(GetLastError()))); } len = x; return x; } virtual size_t write(const void* buf, size_t len) throw(Exception) { DWORD x; if(!::WriteFile(h, buf, (DWORD)len, &x, NULL)) { throw FileException(Util::translateError(GetLastError())); } dcassert(x == len); return x; } virtual void setEOF() throw(FileException) { dcassert(isOpen()); if(!SetEndOfFile(h)) { throw FileException(Util::translateError(GetLastError())); } } virtual size_t flush() throw(Exception) { if(!FlushFileBuffers(h)) throw FileException(Util::translateError(GetLastError())); return 0; } static void deleteFile(const string& aFileName) throw() { ::DeleteFile(Text::toT(aFileName).c_str()); } static void renameFile(const string& source, const string& target) throw(FileException) { if(!::MoveFile(Text::toT(source).c_str(), Text::toT(target).c_str())) { // Can't move, try copy/delete... if(!CopyFile(Text::toT(source).c_str(), Text::toT(target).c_str(), FALSE)) { throw FileException(Util::translateError(GetLastError())); } deleteFile(source); } } static void copyFile(const string& src, const string& target) throw(FileException) { if(!::CopyFile(Text::toT(src).c_str(), Text::toT(target).c_str(), FALSE)) { throw FileException(Util::translateError(GetLastError())); } } static int64_t getSize(const string& aFileName) throw() { WIN32_FIND_DATA fd; HANDLE hFind; hFind = FindFirstFile(Text::toT(aFileName).c_str(), &fd); if (hFind == INVALID_HANDLE_VALUE) { return -1; } else { FindClose(hFind); return ((int64_t)fd.nFileSizeHigh << 32 | (int64_t)fd.nFileSizeLow); } } static void ensureDirectory(const string& aFile) { // Skip the first dir... tstring file; Text::toT(aFile, file); wstring::size_type start = file.find_first_of(L"\\/"); if(start == string::npos) return; start++; while( (start = file.find_first_of(L"\\/", start)) != string::npos) { CreateDirectory(file.substr(0, start+1).c_str(), NULL); start++; } } #else // _WIN32 enum { READ = 0x01, WRITE = 0x02, RW = READ | WRITE, }; File(const string& aFileName, int access, int mode) throw(FileException) { dcassert(access == WRITE || access == READ || access == (READ | WRITE)); 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; } h = open(aFileName.c_str(), m, S_IRUSR | S_IWUSR); if(h == -1) throw FileException("Could not open file"); } u_int32_t getLastModified() { struct stat s; if (::fstat(h, &s) == -1) return 0; return (u_int32_t)s.st_mtime; } bool isOpen() { return h != -1; }; virtual void close() throw(FileException) { if(h != -1) { ::close(h); h = -1; } } virtual int64_t getSize() throw(FileException) { struct stat s; if(::fstat(h, &s) == -1) return -1; return (int64_t)s.st_size; } virtual int64_t getPos() throw(FileException) { return (int64_t) lseek(h, 0, SEEK_CUR); } virtual void setPos(int64_t pos) throw(FileException) { lseek(h, (off_t)pos, SEEK_SET); }; virtual void setEndPos(int64_t pos) throw(FileException) { lseek(h, (off_t)pos, SEEK_END); }; virtual void movePos(int64_t pos) throw(FileException) { lseek(h, (off_t)pos, SEEK_CUR); }; virtual size_t read(void* buf, size_t& len) throw(FileException) { ssize_t x = ::read(h, buf, len); if(x == -1) throw("Read error"); len = x; return (size_t)x; } virtual size_t write(const void* buf, size_t len) throw(FileException) { ssize_t x = ::write(h, buf, len); if(x == -1) throw FileException("Write error"); if(x < (ssize_t)len) throw FileException("Disk full(?)"); return x; } virtual void setEOF() throw(FileException) { if(ftruncate(h, (off_t)getPos()) == -1) throw FileException(Util::translateError(errno)); } virtual void setSize(int64_t newSize) throw(FileException) { if(ftruncate(h, (off_t)newSize) == -1) throw FileException(Util::translateError(errno)); } virtual size_t flush() throw(Exception) { if(fsync(h) == -1) throw FileException(Util::translateError(errno)); return 0; } static void deleteFile(const string& aFileName) throw() { ::unlink(aFileName.c_str()); }; static void renameFile(const string& source, const string& target) throw() { ::rename(source.c_str(), target.c_str()); }; static void copyFile(const string& source, const string& target) throw() { #warning FIXME } static int64_t getSize(const string& aFileName) { struct stat s; if(stat(aFileName.c_str(), &s) == -1) return -1; return s.st_size; } static void ensureDirectory(const string& aFile) { string acp = Text::utf8ToAcp(aFile); string::size_type start = 0; while( (start = aFile.find_first_of(L'/', start)) != string::npos) { mkdir(aFile.substr(0, start+1).c_str(), 0755); start++; } } #endif // _WIN32 virtual ~File() throw(FileException) { File::close(); } string read(size_t len) throw(FileException) { string s(len, 0); size_t x = read(&s[0], len); if(x != len) s.resize(x); return s; } string read() throw(FileException) { setPos(0); int64_t sz = getSize(); if(sz == -1) return Util::emptyString; return read((u_int32_t)sz); } void write(const string& aString) throw(FileException) { write((void*)aString.data(), aString.size()); }; protected: #ifdef _WIN32 HANDLE h; #else int h; #endif private: File(const File&); File& operator=(const File&); }; template class LimitedInputStream : public InputStream { public: LimitedInputStream(InputStream* is, int64_t aMaxBytes) : s(is), maxBytes(aMaxBytes) { } virtual ~LimitedInputStream() { if(managed) delete s; } size_t read(void* buf, size_t& len) throw(FileException) { dcassert(maxBytes >= 0); len = (size_t)min(maxBytes, (int64_t)len); if(len == 0) return 0; size_t x = s->read(buf, len); maxBytes -= x; return x; } private: InputStream* s; int64_t maxBytes; }; template class BufferedOutputStream : public OutputStream { public: using OutputStream::write; BufferedOutputStream(OutputStream* aStream, size_t aBufSize = SETTING(BUFFER_SIZE) * 1024) : s(aStream), pos(0), bufSize(aBufSize), buf(new u_int8_t[bufSize]) { } virtual ~BufferedOutputStream() { try { // We must do this in order not to lose bytes when a download // is disconnected prematurely flush(); } catch(const Exception&) { } if(managed) delete s; delete buf; } virtual size_t flush() throw(Exception) { if(pos > 0) s->write(buf, pos); pos = 0; s->flush(); return 0; } virtual size_t write(const void* wbuf, size_t len) throw(Exception) { u_int8_t* b = (u_int8_t*)wbuf; size_t l2 = len; while(len > 0) { if(pos == 0 && len >= bufSize) { s->write(b, len); break; } else { size_t n = min(bufSize - pos, len); memcpy(buf + pos, b, n); b += n; pos += n; len -= n; if(pos == bufSize) { s->write(buf, bufSize); pos = 0; } } } return l2; } private: OutputStream* s; size_t pos; size_t bufSize; u_int8_t* buf; }; class StringOutputStream : public OutputStream { public: virtual size_t flush() throw(Exception) { return 0; } virtual size_t write(const void* buf, size_t len) throw(Exception) { str.append((char*)buf, len); return len; } const string& getString() { return str; } private: string str; }; #endif // !defined(AFX_FILE_H__CB551CD7_189C_4175_922E_8B00B4C8D6F1__INCLUDED_) /** * @file * $Id: File.h,v 1.42 2004/09/25 20:40:39 arnetheduck Exp $ */